ส่งคืนค่าจากเธรด?


คำตอบ:


94

วิธีที่ง่ายที่สุดวิธีหนึ่งในการรับค่าส่งคืนจากเธรดคือการใช้การปิด สร้างตัวแปรที่จะเก็บค่าที่ส่งคืนจากเธรดแล้วจับในนิพจน์แลมบ์ดา กำหนดค่า "ส่งคืน" ให้กับตัวแปรนี้จากเธรดผู้ปฏิบัติงานจากนั้นเมื่อเธรดสิ้นสุดคุณสามารถใช้จากเธรดแม่

void Main()
{
  object value = null; // Used to store the return value
  var thread = new Thread(
    () =>
    {
      value = "Hello World"; // Publish the return value
    });
  thread.Start();
  thread.Join();
  Console.WriteLine(value); // Use the return value here
}

3
จะไม่lock(value) { value = "Hello world"; }ดีกว่าในการจัดการการเขียนค่าเธรดหลายค่า?
เช็ค

4
@checksum: ในกรณีนี้ไม่จำเป็นเนื่องจากไม่มีการอ่านหรือเขียนvalueเกิดขึ้นในเวลาเดียวกัน แต่โปรดระวังเสมอเมื่อจำเป็นต้องล็อก
Brian Gideon

ไอเดียสุดล้ำ! ทำงานได้อย่างยอดเยี่ยมและควรเป็นคำตอบที่ได้รับการยอมรับ
MerseyViking

34

ขึ้นอยู่กับว่าคุณต้องการสร้างเธรดและเวอร์ชัน. NET ที่พร้อมใช้งานอย่างไร:

.NET 2.0+:

A) คุณสามารถสร้างThreadวัตถุได้โดยตรง ในกรณีนี้คุณสามารถใช้ "closure" - ประกาศตัวแปรและบันทึกโดยใช้ lambda-expression:

object result = null;
Thread thread = new System.Threading.Thread(() => { 
    //Some work...
    result = 42; });
thread.Start();
thread.Join();
Console.WriteLine(result);

B) คุณสามารถใช้ผู้รับมอบสิทธิ์และIAsyncResultและส่งคืนค่าจากEndInvoke()วิธีการ:

delegate object MyFunc();
...
MyFunc x = new MyFunc(() => { 
    //Some work...
    return 42; });
IAsyncResult asyncResult = x.BeginInvoke(null, null);
object result = x.EndInvoke(asyncResult);

C) คุณสามารถใช้BackgroundWorkerคลาส ในกรณีนี้คุณสามารถใช้ตัวแปรที่จับ (เช่นเดียวกับThreadวัตถุ) หรือจัดการRunWorkerCompletedเหตุการณ์:

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (s, e) => {
    //Some work...
    e.Result = 42;
};
worker.RunWorkerCompleted += (s, e) => {
    //e.Result "returned" from thread
    Console.WriteLine(e.Result);
};
worker.RunWorkerAsync();

.NET 4.0+:

เริ่มต้นด้วย. NET 4.0 คุณสามารถใช้Task Parallel LibraryและTaskclass เพื่อเริ่มเธรดของคุณได้ คลาสทั่วไปTask<TResult>ช่วยให้คุณได้รับค่าตอบแทนจากResultคุณสมบัติ:

//Main thread will be blocked until task thread finishes
//(because of obtaining the value of the Result property)
int result = Task.Factory.StartNew(() => {
    //Some work...
    return 42;}).Result;

.NET 4.5+:

เริ่มต้นด้วย. NET 4.5 คุณยังสามารถใช้async/ awaitคีย์เวิร์ดเพื่อส่งคืนค่าจากงานโดยตรงแทนที่จะได้รับResultคุณสมบัติ:

int result = await Task.Run(() => {
    //Some work...
    return 42; });

หมายเหตุ: วิธีการซึ่งมีรหัสด้านบน shoud จะถูกทำเครื่องหมายด้วยasyncคำหลัก

ด้วยเหตุผลหลายประการการใช้ Task Parallel Library เป็นวิธีที่ดีกว่าในการทำงานกับเธรด


33

ฉันจะใช้วิธีBackgroundWorkerและส่งคืนผลลัพธ์ใน e.Result

แก้ไข:

สิ่งนี้มักเกี่ยวข้องกับ WinForms และ WPF แต่สามารถใช้ได้กับแอปพลิเคชัน. NET ทุกประเภท นี่คือโค้ดตัวอย่างสำหรับแอปคอนโซลที่ใช้ BackgroundWorker:

using System;
using System.Threading;
using System.ComponentModel;
using System.Collections.Generic;
using System.Text;

namespace BGWorker
{
    class Program
    {
        static bool done = false;

        static void Main(string[] args)
        {
            BackgroundWorker bg = new BackgroundWorker();
            bg.DoWork += new DoWorkEventHandler(bg_DoWork);
            bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_RunWorkerCompleted);
            bg.RunWorkerAsync();

            while (!done)
            {
                Console.WriteLine("Waiting in Main, tid " + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(100);
            }
        }

        static void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            Console.WriteLine("Completed, tid " + Thread.CurrentThread.ManagedThreadId);
            done = true;
        }

        static void bg_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 1; i <= 5; i++)
            {
                Console.WriteLine("Work Line: " + i + ", tid " + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(500);
            }
        }
    }
}

เอาท์พุต:

Waiting in Main, tid 10
Work Line: 1, tid 6
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Work Line: 2, tid 6
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Work Line: 3, tid 6
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Work Line: 4, tid 6
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Work Line: 5, tid 6
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Waiting in Main, tid 10
Completed, tid 6

อัพเดท 2014

ดูคำตอบของ @ Roger ด้านล่าง

https://stackoverflow.com/a/24916747/141172

เขาชี้ให้เห็นว่าคุณสามารถใช้งานที่ส่งกลับและเช็คอินTask<T>Task<T>.Result


ใช่ แต่ใช้ได้กับ WinForms และ WPF เท่านั้น
Henk Holterman

@ เฮ็งค์: ไม่จริง ฉันเพิ่งเขียนแอปคอนโซลง่ายๆที่ใช้ BackgroundWorker เพื่อให้แน่ใจว่า :-) แก้ไขโพสต์ของฉันด้วยรหัสนั้น
Eric J.

Eric ใส่ข้อความเขียนไว้ในโค้ดของคุณเพื่อดูว่าเกิดอะไรขึ้นและ ThreadId อะไร มันอาจไม่เป็นไปตามที่คุณคาดหวัง (เสร็จสมบูรณ์จะทำงานก่อนที่ Dowork จะเสร็จสิ้นและไม่อยู่ในเธรดหลัก) Bgw ต้องการ MessagePump
Henk Holterman

@ เฮ็งค์: คุณถูกครึ่งหนึ่ง เสร็จสิ้นการรันบนเธรดเดียวกันกับ BackgroundWorker แต่รันหลังจาก DoWork เสร็จสมบูรณ์ ดูผลลัพธ์ในคำตอบที่แก้ไข
Eric J.

2
ไม่มีเงื่อนไขการแข่งขันเนื่องจากเธรดหนึ่งชุดตัวแปรและเธรดหนึ่งตัวอ่านมันและลำดับที่แน่นอนของชุดเทียบกับการอ่านไม่สำคัญกับการเรียกใช้โค้ดที่ถูกต้อง (กล่าวคือเงื่อนไขการสิ้นสุดอาจเกิดขึ้นในเธรดหลัก เร็วกว่าหรือช้ากว่าเล็กน้อยขึ้นอยู่กับลำดับที่เธรดถูกกำหนดเวลาไว้ แต่วิธีใดก็ตามที่คุณยังคงได้ผลลัพธ์ที่ถูกต้อง)
Eric J.

21

เธรดไม่ใช่วิธีการ - โดยปกติคุณจะไม่ "ส่งคืน" ค่า

อย่างไรก็ตามหากคุณพยายามดึงค่ากลับมาจากผลลัพธ์ของการประมวลผลบางอย่างคุณมีตัวเลือกมากมายโดยมีสองตัวเลือกหลักดังนี้

  • คุณสามารถซิงโครไนซ์ข้อมูลที่แชร์และตั้งค่าให้เหมาะสม
  • คุณยังสามารถส่งข้อมูลกลับในรูปแบบการโทรกลับบางรูปแบบ

ขึ้นอยู่กับว่าคุณสร้างเธรดอย่างไรและคุณต้องการใช้งานอย่างไรรวมถึงภาษา / กรอบงาน / เครื่องมือที่คุณใช้


15

คลาสโปรดของฉันเรียกใช้เมธอดใด ๆ บนเธรดอื่นด้วยโค้ดเพียง 2 บรรทัด

class ThreadedExecuter<T> where T : class
{
    public delegate void CallBackDelegate(T returnValue);
    public delegate T MethodDelegate();
    private CallBackDelegate callback;
    private MethodDelegate method;

    private Thread t;

    public ThreadedExecuter(MethodDelegate method, CallBackDelegate callback)
    {
        this.method = method;
        this.callback = callback;
        t = new Thread(this.Process);
    }
    public void Start()
    {
        t.Start();
    }
    public void Abort()
    {
        t.Abort();
        callback(null); //can be left out depending on your needs
    }
    private void Process()
    {
        T stuffReturned = method();
        callback(stuffReturned);
    }
}

การใช้งาน

    void startthework()
    {
        ThreadedExecuter<string> executer = new ThreadedExecuter<string>(someLongFunction, longFunctionComplete);
        executer.Start();
    }
    string someLongFunction()
    {
        while(!workComplete)
            WorkWork();
        return resultOfWork;
    }
    void longFunctionComplete(string s)
    {
        PrintWorkComplete(s);
    }

ระวังว่า longFunctionComplete จะไม่ทำงานบนเธรดเดียวกับ starthework

สำหรับวิธีการใช้พารามิเตอร์คุณสามารถใช้การปิดหรือขยายคลาสได้ตลอดเวลา


3
ไม่ชัดเจนสำหรับทุกคน ... stuffReturned?, resultOfWork, PrintWorkComplete? ฯลฯ
Lost_In_Library

14

นี่คือตัวอย่างง่ายๆโดยใช้ผู้รับมอบสิทธิ์ ...

void Main()
{
   DoIt d1 = Doer.DoThatThang;
   DoIt d2 = Doer.DoThatThang;

   IAsyncResult r1 = d1.BeginInvoke( 5, null, null );
   IAsyncResult r2 = d2.BeginInvoke( 10, null, null );

   Thread.Sleep( 1000 );

   var s1 = d1.EndInvoke( r1 );
   var s2 = d2.EndInvoke( r2 );

   s1.Dump(); // You told me 5
   s2.Dump(); // You told me 10
}

public delegate string DoIt( int x );

public class Doer
{
  public static string DoThatThang( int x  )
  {
    return "You told me " + x.ToString();
  }
}

มีชุดที่ยอดเยี่ยมในเธรดที่เป็นเธรดใน C #


10

เพียงใช้วิธีการมอบหมาย

int val;
Thread thread = new Thread(() => { val = Multiply(1, 2); });
thread.Start();

ตอนนี้ให้ทำฟังก์ชัน Multiply ที่จะทำงานกับเธรดอื่น:

int Multiply(int x, int y)
{
    return x * y;
}

3
เหตุใดจึงมีคำตอบด้านล่าง "บันทึกลงในไฟล์ข้อความและเรียกคืน"
จอน

7

ฉันเจอเธรดนี้เมื่อพยายามรับค่าส่งคืนของวิธีการที่ดำเนินการภายในเธรด ฉันคิดว่าฉันจะโพสต์วิธีแก้ปัญหาที่ได้ผล

โซลูชันนี้ใช้คลาสเพื่อเก็บทั้งวิธีที่จะดำเนินการ (ทางอ้อม) และเก็บค่าที่ส่งคืน คลาสสามารถใช้สำหรับฟังก์ชันใดก็ได้และประเภทการส่งคืนใด ๆ คุณเพียงแค่สร้างอินสแตนซ์อ็อบเจ็กต์โดยใช้ชนิดค่าส่งคืนจากนั้นส่งผ่านฟังก์ชันเพื่อเรียกผ่านแลมบ์ดา (หรือผู้รับมอบสิทธิ์)


การใช้งาน C # 3.0


public class ThreadedMethod<T>
{

    private T mResult;
    public T Result 
    {
        get { return mResult; }
        private set { mResult = value; }
    }

    public ThreadedMethod()
    {
    }

    //If supporting .net 3.5
    public void ExecuteMethod(Func<T> func)
    {
        Result = func.Invoke();
    }

    //If supporting only 2.0 use this and 
    //comment out the other overload
    public void ExecuteMethod(Delegate d)
    {
        Result = (T)d.DynamicInvoke();
    }
}

ในการใช้รหัสนี้คุณสามารถใช้ Lambda (หรือผู้รับมอบสิทธิ์) นี่คือตัวอย่างการใช้ lambdas:

ThreadedMethod<bool> threadedMethod = new ThreadedMethod<bool>();
Thread workerThread = new Thread((unused) => 
                            threadedMethod.ExecuteMethod(() => 
                                SomeMethod()));
workerThread.Start();
workerThread.Join();
if (threadedMethod.Result == false) 
{
    //do something about it...
}

การใช้งาน VB.NET 2008


ทุกคนที่ใช้ VB.NET 2008 ไม่สามารถใช้ lambdas กับวิธีการส่งคืนที่ไม่ใช่ค่าได้ สิ่งนี้มีผลต่อThreadedMethodคลาสดังนั้นเราจะExecuteMethodคืนค่าของฟังก์ชัน นี่ไม่เจ็บอะไรเลย

Public Class ThreadedMethod(Of T)

    Private mResult As T
    Public Property Result() As T
        Get
            Return mResult
        End Get
        Private Set(ByVal value As T)
            mResult = value
        End Set
    End Property

    Sub New()
    End Sub

    'If supporting .net 3.5'
    Function ExecuteMethod(ByVal func As Func(Of T)) As T
        Result = func.Invoke()
        Return Result
    End Function

    'If supporting only 2.0 use this and' 
    'comment out the other overload'
    Function ExecuteMethod(ByVal d As [Delegate]) As T
        Result = DirectCast(d.DynamicInvoke(), T)
        Return Result
    End Function

End Class

7

ด้วย. NET Framework ล่าสุดคุณสามารถส่งคืนค่าจากเธรดที่แยกจากกันโดยใช้งานโดยที่คุณสมบัติผลลัพธ์จะบล็อกเธรดการเรียกจนกว่างานจะเสร็จสิ้น:

  Task<MyClass> task = Task<MyClass>.Factory.StartNew(() =>
  {
      string s = "my message";
      double d = 3.14159;
      return new MyClass { Name = s, Number = d };
  });
  MyClass test = task.Result;

สำหรับรายละเอียดโปรดดูhttp://msdn.microsoft.com/en-us/library/dd537613(v=vs.110).aspx


5

ผู้ร่วมประชุม ThreadStart ใน C # ที่ใช้ในการเริ่มเธรดมีการส่งคืนประเภท 'โมฆะ'

หากคุณต้องการรับ 'ค่าส่งคืน' จากเธรดคุณควรเขียนไปยังตำแหน่งที่ใช้ร่วมกัน (ในลักษณะที่เหมาะสมกับเธรดที่ปลอดภัย) และอ่านจากนั้นเมื่อเธรดเสร็จสิ้นการดำเนินการ


5

หากคุณไม่ต้องการใช้ BackgroundWorker และเพียงแค่ใช้เธรดปกติคุณสามารถเริ่มเหตุการณ์เพื่อส่งคืนข้อมูลเช่นนี้:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace ThreadWithDataReturnExample
{
    public partial class Form1 : Form
    {
        private Thread thread1 = null;

        public Form1()
        {
            InitializeComponent();

            thread1 = new Thread(new ThreadStart(this.threadEntryPoint));
            Thread1Completed += new AsyncCompletedEventHandler(thread1_Thread1Completed);
        }

        private void startButton_Click(object sender, EventArgs e)
        {
            thread1.Start();
            //Alternatively, you could pass some object
            //in such as Start(someObject);
            //With apprioriate locking, or protocol where
            //no other threads access the object until
            //an event signals when the thread is complete,
            //any other class with a reference to the object 
            //would be able to access that data.
            //But instead, I'm going to use AsyncCompletedEventArgs 
            //in an event that signals completion
        }

        void thread1_Thread1Completed(object sender, AsyncCompletedEventArgs e)
        {
            if (this.InvokeRequired)
            {//marshal the call if we are not on the GUI thread                
                BeginInvoke(new AsyncCompletedEventHandler(thread1_Thread1Completed),
                  new object[] { sender, e });
            }
            else
            {
                //display error if error occurred
                //if no error occurred, process data
                if (e.Error == null)
                {//then success

                    MessageBox.Show("Worker thread completed successfully");
                    DataYouWantToReturn someData = e.UserState as DataYouWantToReturn;
                    MessageBox.Show("Your data my lord: " + someData.someProperty);

                }
                else//error
                {
                    MessageBox.Show("The following error occurred:" + Environment.NewLine + e.Error.ToString());
                }
            }
        }

        #region I would actually move all of this into it's own class
            private void threadEntryPoint()
            {
                //do a bunch of stuff

                //when you are done:
                //initialize object with data that you want to return
                DataYouWantToReturn dataYouWantToReturn = new DataYouWantToReturn();
                dataYouWantToReturn.someProperty = "more data";

                //signal completion by firing an event
                OnThread1Completed(new AsyncCompletedEventArgs(null, false, dataYouWantToReturn));
            }

            /// <summary>
            /// Occurs when processing has finished or an error occurred.
            /// </summary>
            public event AsyncCompletedEventHandler Thread1Completed;
            protected virtual void OnThread1Completed(AsyncCompletedEventArgs e)
            {
                //copy locally
                AsyncCompletedEventHandler handler = Thread1Completed;
                if (handler != null)
                {
                    handler(this, e);
                }
            }
        #endregion

    }
}

ฉันแก้ไขรายละเอียดเล็กน้อยในรหัสของคุณ ดูเหมือนว่าคุณซ้ายปิดthread1_ส่วนบนสายไฟของเขาขึ้นของAsyncCompletedEventHandler หากการแก้ไขของฉันมีข้อผิดพลาดโปรดช่วยฉันทำความเข้าใจว่าเกิดอะไรขึ้นที่นั่น
jp2code

1
@ jp2code คุณไม่สามารถทำได้thread1_Thread1Completed +=เนื่องจากthread1_Thread1Completedเป็นชื่อของฟังก์ชันดังนั้นคุณจึงไม่สามารถวางไว้ที่ด้านซ้ายมือของตัวดำเนินการกำหนดได้ ทางซ้ายมือThread1Completed +=จะใช้เนื่องจากเป็นเหตุการณ์ดังนั้นจึงสามารถปรากฏทางด้านซ้ายมือของตัวดำเนินการมอบหมายเพื่อเพิ่มตัวจัดการเหตุการณ์ ดูpublic event AsyncCompletedEventHandler Thread1Completed;
AaronLS

ฉันเห็นมันแล้ว ฉันไม่รู้ว่าทำไมฉันไม่เห็นตัวจัดการเหตุการณ์นั้นใน#regionส่วนของคุณมาก่อน ฉันมอง ซื่อสัตย์! :)
jp2code

2

เธรดไม่มีค่าส่งคืนจริงๆ อย่างไรก็ตามหากคุณสร้างผู้รับมอบสิทธิ์คุณสามารถเรียกใช้แบบอะซิงโครนัสผ่านBeginInvokeวิธีการ สิ่งนี้จะเรียกใช้เมธอดบนเธรดพูลเธรด คุณจะได้รับค่าตอบแทนใด ๆ EndInvokeจากเช่นการโทรผ่าน

ตัวอย่าง:

static int GetAnswer() {
   return 42;
}

...

Func<int> method = GetAnswer;
var res = method.BeginInvoke(null, null); // provide args as needed
var answer = method.EndInvoke(res);

GetAnswerจะดำเนินการกับเธรดพูลเธรดและเมื่อเสร็จสิ้นคุณสามารถดึงคำตอบผ่านEndInvokeดังที่แสดง


2

BackgroundWorkerเป็นสิ่งที่ดีเมื่อมีการพัฒนาสำหรับ Windows แบบฟอร์ม

สมมติว่าคุณต้องการผ่านชั้นเรียนง่ายๆไปมา:

class Anything {
    // Number and Text are for instructional purposes only
    public int Number { get; set; }
    public string Text { get; set; }
    // Data can be any object - even another class
    public object Data { get; set; }
}

ฉันเขียนคลาสสั้น ๆ ที่ทำสิ่งต่อไปนี้:

  • สร้างหรือล้างรายการ
  • เริ่มวนซ้ำ
  • ในลูปสร้างรายการใหม่สำหรับรายการ
  • สร้างเธรดในลูป
  • ในลูปส่งรายการเป็นพารามิเตอร์ไปยังเธรด
  • ในลูปเริ่มต้นเธรด
  • ในลูปเพิ่มเธรดในรายการเพื่อรับชม
  • หลังจากวนซ้ำเข้าร่วมแต่ละเธรด
  • หลังจากการรวมทั้งหมดเสร็จสิ้นแล้วให้แสดงผลลัพธ์

จากภายในรูทีนของเธรด:

  • ล็อคการโทรเพื่อให้มีเพียง 1 เธรดเท่านั้นที่สามารถเข้าสู่รูทีนนี้ได้ในแต่ละครั้ง (ต้องรอ)
  • โพสต์ข้อมูลเกี่ยวกับรายการ
  • แก้ไขรายการ
  • เมื่อเธรดเสร็จสิ้นข้อมูลจะแสดงบนคอนโซล

การเพิ่มผู้รับมอบสิทธิ์อาจเป็นประโยชน์สำหรับการโพสต์ข้อมูลของคุณกลับไปยังเธรดหลักของคุณโดยตรง แต่คุณอาจต้องใช้Invokeหากรายการข้อมูลบางรายการไม่ปลอดภัย

class AnyTask {

    private object m_lock;

    public AnyTask() {
        m_lock = new object();
    }
    // Something to use the delegate
    public event MainDelegate OnUpdate;

    public void Test_Function(int count) {
        var list = new List<Thread>(count);
        for (var i = 0; i < count; i++) {
            var thread = new Thread(new ParameterizedThreadStart(Thread_Task));
            var item = new Anything() {
                Number = i,
                Text = String.Format("Test_Function #{0}", i)
            };
            thread.Start(item);
            list.Add(thread);
        }
        foreach (var thread in list) {
            thread.Join();
        }
    }

    private void MainUpdate(Anything item, bool original) {
        if (OnUpdate != null) {
            OnUpdate(item, original);
        }
    }

    private void Thread_Task(object parameter) {
        lock (m_lock) {
            var item = (Anything)parameter;
            MainUpdate(item, true);
            item.Text = String.Format("{0}; Thread_Task #{1}", item.Text, item.Number);
            item.Number = 0;
            MainUpdate(item, false);
        }
    }

}

ในการทดสอบสิ่งนี้ให้สร้างแอปพลิเคชันคอนโซลเล็กน้อยและใส่ไว้ในไฟล์Program.cs :

// A delegate makes life simpler
delegate void MainDelegate(Anything sender, bool original);

class Program {

    private const int COUNT = 15;
    private static List<Anything> m_list;

    static void Main(string[] args) {
        m_list = new List<Anything>(COUNT);
        var obj = new AnyTask();
        obj.OnUpdate += new MainDelegate(ThreadMessages);
        obj.Test_Function(COUNT);
        Console.WriteLine();
        foreach (var item in m_list) {
            Console.WriteLine("[Complete]:" + item.Text);
        }
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }

    private static void ThreadMessages(Anything item, bool original) {
        if (original) {
            Console.WriteLine("[main method]:" + item.Text);
        } else {
            m_list.Add(item);
        }
    }

}

นี่คือภาพหน้าจอของสิ่งที่ฉันได้รับจากสิ่งนี้:

เอาต์พุตคอนโซล

ฉันหวังว่าคนอื่นจะเข้าใจสิ่งที่ฉันพยายามอธิบาย

ฉันสนุกกับการทำงานกับเธรดและการใช้ตัวแทน พวกเขาทำให้ C # สนุกมาก

ภาคผนวก: สำหรับ VB Coders

ฉันต้องการดูสิ่งที่เกี่ยวข้องกับการเขียนโค้ดด้านบนเป็นแอปพลิเคชันคอนโซล VB การแปลงเกี่ยวข้องกับบางสิ่งที่ฉันไม่คาดคิดดังนั้นฉันจะอัปเดตเธรดนี้ที่นี่สำหรับผู้ที่ต้องการทราบวิธีการเธรดใน VB

Imports System.Threading

Delegate Sub MainDelegate(sender As Anything, original As Boolean)

Class Main

    Private Const COUNT As Integer = 15
    Private Shared m_list As List(Of Anything)

    Public Shared Sub Main(args As String())
        m_list = New List(Of Anything)(COUNT)
        Dim obj As New AnyTask()
        AddHandler obj.OnUpdate, New MainDelegate(AddressOf ThreadMessages)
        obj.Test_Function(COUNT)
        Console.WriteLine()
        For Each item As Anything In m_list
            Console.WriteLine("[Complete]:" + item.Text)
        Next
        Console.WriteLine("Press any key to exit.")
        Console.ReadKey()
    End Sub

    Private Shared Sub ThreadMessages(item As Anything, original As Boolean)
        If original Then
            Console.WriteLine("[main method]:" + item.Text)
        Else
            m_list.Add(item)
        End If
    End Sub

End Class

Class AnyTask

    Private m_lock As Object

    Public Sub New()
        m_lock = New Object()
    End Sub
    ' Something to use the delegate
    Public Event OnUpdate As MainDelegate

    Public Sub Test_Function(count As Integer)
        Dim list As New List(Of Thread)(count)
        For i As Int32 = 0 To count - 1
            Dim thread As New Thread(New ParameterizedThreadStart(AddressOf Thread_Task))
            Dim item As New Anything()
            item.Number = i
            item.Text = String.Format("Test_Function #{0}", i)
            thread.Start(item)
            list.Add(thread)
        Next
        For Each thread As Thread In list
            thread.Join()
        Next
    End Sub

    Private Sub MainUpdate(item As Anything, original As Boolean)
        RaiseEvent OnUpdate(item, original)
    End Sub

    Private Sub Thread_Task(parameter As Object)
        SyncLock m_lock
            Dim item As Anything = DirectCast(parameter, Anything)
            MainUpdate(item, True)
            item.Text = [String].Format("{0}; Thread_Task #{1}", item.Text, item.Number)
            item.Number = 0
            MainUpdate(item, False)
        End SyncLock
    End Sub

End Class


Class Anything
    ' Number and Text are for instructional purposes only
    Public Property Number() As Integer
        Get
            Return m_Number
        End Get
        Set(value As Integer)
            m_Number = value
        End Set
    End Property
    Private m_Number As Integer
    Public Property Text() As String
        Get
            Return m_Text
        End Get
        Set(value As String)
            m_Text = value
        End Set
    End Property
    Private m_Text As String
    ' Data can be anything or another class
    Public Property Data() As Object
        Get
            Return m_Data
        End Get
        Set(value As Object)
            m_Data = value
        End Set
    End Property
    Private m_Data As Object
End Class

1
class Program
{
    static void Main(string[] args)
    {
        string returnValue = null;
       new Thread(
          () =>
          {
              returnValue =test() ; 
          }).Start();
        Console.WriteLine(returnValue);
        Console.ReadKey();
    }

    public static string test()
    {
        return "Returning From Thread called method";
    }
}

ตัวอย่างที่ให้มาไม่ถูกต้องคุณโชคดีที่ได้ผลสำหรับคุณ test(){ Thread.Sleep(5000); /*Highly time demanding process*/ return "Returned from test()";}จินตนาการสถานการณ์ต่อไปนี้ ในกรณีนี้เธรดแบบสแตนด์อโลนจะไม่มีเวลากำหนดค่าใหม่ให้กับreturnValueตัวแปร ในฐานะที่เป็นที่พึ่งสุดท้ายคุณสามารถประหยัดอ้างอิงด้ายและหลังจากนั้นเริ่มต้นในทางตรงกันvar standaloneThread = new Thread(()=> //...); standaloneThread.Start(); standaloneThread.Join();แต่นี่ไม่ใช่แนวทางปฏิบัติที่ดีที่สุดอย่างแน่นอน
AlexMelw

1

วิธีแก้ปัญหาง่ายๆคือการส่งผ่านพารามิเตอร์โดยอ้างถึงฟังก์ชันที่รันอยู่ในเธรดและเปลี่ยนค่าในเธรด

       // create a list of threads
        List<Thread> threads = new List<Thread>();


        //declare the ref params
        bool is1 = false;
        bool is2 = false;

        threads.Add(new Thread(() => myFunction(someVar, ref is1)));
        threads.Add(new Thread(() => myFunction(someVar, ref is2)));

        threads.ForEach(x => x.Start());

        // wait for threads to finish
        threads.ForEach(x => x.Join());

        //check the ref params
        if (!is1)
        {
          //do something
        }

        if (!is2)
        {
           //do somethign else
        }

หากคุณไม่สามารถเปลี่ยนฟังก์ชันที่ทำงานอยู่ในดอกยางได้คุณสามารถตัดฟังก์ชันอื่นได้:

 bool theirFunction(var someVar){
   return false;
}


 void myFunction(var someVar ref bool result){
  result = theirFunction(myVar);
 }

โปรดอธิบายการลงคะแนน ฉันใช้รูปแบบนี้ในรหัสของฉันเองและมันก็ทำงานได้ดีอย่างสมบูรณ์
CodeToad

1

สามารถใช้รหัสนี้:

 private Object MyThread(Object Data)
      {
        Object response = null;
        Thread newThread = new Thread(() =>
        {
            response = MyFunction(Data);
            //MyFunction Is Function that you Define
        });
        newThread.Start();
        newThread.Join();
        return response;
      }

-1

ฉันไม่ใช่ผู้เชี่ยวชาญในการทำเธรดนั่นคือเหตุผลที่ฉันทำแบบนี้:

ฉันสร้างไฟล์การตั้งค่าและ

ภายในเธรดใหม่:

Setting.Default.ValueToBeSaved;
Setting.Default.Save();

จากนั้นฉันก็รับค่านั้นเมื่อใดก็ตามที่ฉันต้องการ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.