Mengembalikan nilai dari utas?


Jawaban:


94

Salah satu cara termudah untuk mendapatkan nilai kembali dari utas adalah dengan menggunakan closure. Buat variabel yang akan menyimpan nilai yang dikembalikan dari utas, lalu tangkap dalam ekspresi lambda. Tetapkan nilai "return" ke variabel ini dari utas pekerja, lalu setelah utas itu berakhir, Anda dapat menggunakannya dari utas induk.

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
Bukankah lock(value) { value = "Hello world"; }lebih baik dalam menangani penulisan nilai beberapa utas?
checksum

4
@checksum: Dalam kasus khusus ini, ini tidak perlu karena tidak ada pembacaan atau penulisan valueyang terjadi pada waktu yang sama. Tapi, ya, selalu perhatikan kapan kunci diperlukan.
Brian Gideon

Ide yang fantastis! Bekerja dengan sangat baik, dan harus menjadi jawaban yang diterima.
MerseyViking

34

Itu tergantung pada bagaimana Anda ingin membuat utas dan versi .NET yang tersedia:

.NET 2.0+:

A) Anda dapat membuat Threadobjek secara langsung. Dalam hal ini Anda dapat menggunakan "closure" - mendeklarasikan variabel dan menangkapnya menggunakan lambda-expression:

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

B) Anda dapat menggunakan delegasi dan IAsyncResultdan mengembalikan nilai dari EndInvoke()metode:

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

C) Anda dapat menggunakan BackgroundWorkerkelas. Dalam hal ini Anda dapat menggunakan variabel yang ditangkap (seperti dengan Threadobjek) atau menangani RunWorkerCompletedperistiwa:

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+:

Mulai dengan .NET 4.0 Anda dapat menggunakan Perpustakaan Paralel Tugas dan Taskkelas untuk memulai utas Anda. Kelas generik Task<TResult>memungkinkan Anda mendapatkan nilai kembali dari Resultproperti:

//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+:

Dimulai dengan .NET 4.5 Anda juga bisa menggunakan async/ awaitkeywords untuk mengembalikan nilai dari tugas secara langsung alih-alih mendapatkan Resultproperti:

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

Catatan: metode yang berisi kode di atas harus ditandai dengan asynckata kunci.

Untuk banyak alasan penggunaan Task Parallel Library adalah cara yang lebih disukai untuk bekerja dengan utas.


33

Saya akan menggunakan pendekatan BackgroundWorker dan mengembalikan hasilnya ke e.Result.

EDIT:

Ini biasanya terkait dengan WinForms dan WPF, tetapi dapat digunakan oleh semua jenis aplikasi .NET. Berikut kode contoh untuk aplikasi konsol yang menggunakan 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);
            }
        }
    }
}

Keluaran:

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

PEMBARUAN 2014

Lihat jawaban @ Roger di bawah.

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

Dia menunjukkan bahwa Anda dapat menggunakan Tugas yang mengembalikan a Task<T>, dan periksa Task<T>.Result.


Ya, tetapi ini hanya berlaku untuk WinForms dan WPF.
Henk Holterman

@Henk: Tidak benar. Saya baru saja menulis aplikasi konsol sederhana yang menggunakan BackgroundWorker hanya untuk memastikan :-) Mengedit posting saya dengan kode itu.
Eric J.

Eric, letakkan beberapa garis tulis di kode Anda untuk melihat kapan apa yang terjadi, dan pada ThreadId apa. Ini mungkin tidak berjalan seperti yang Anda harapkan. (Selesai akan berjalan sebelum Dowork selesai, dan bukan di utas Utama). Bgw membutuhkan MessagePump.
Henk Holterman

@ Henk: Anda setengah benar. Selesai berjalan di utas yang sama dengan BackgroundWorker, tetapi berjalan setelah DoWork selesai. Lihat keluaran dalam jawaban yang diedit.
Eric J.

2
Tidak ada kondisi perlombaan, karena persis satu utas menetapkan variabel dan tepat satu utas membacanya, dan urutan persis set vs baca tidak masalah untuk eksekusi kode yang benar (yaitu kondisi penghentian mungkin terjadi di utas utama sedikit lebih awal atau lebih lambat tergantung pada urutan utas dijadwalkan, tetapi Anda masih mendapatkan hasil yang benar).
Eric J.

21

Utas bukanlah metode - Anda biasanya tidak "mengembalikan" nilai.

Namun, jika Anda mencoba mengambil kembali nilai dari hasil beberapa pemrosesan, Anda memiliki banyak opsi, dua yang utama adalah:

  • Anda dapat menyinkronkan bagian data yang dibagikan, dan mengaturnya dengan tepat.
  • Anda juga dapat meneruskan data kembali dalam beberapa bentuk panggilan balik.

Ini sangat tergantung pada bagaimana Anda membuat utas, dan bagaimana Anda ingin menggunakannya, serta bahasa / kerangka / alat yang Anda gunakan.


15

Kelas favorit saya, menjalankan metode apa pun di utas lain hanya dengan 2 baris kode.

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);
    }
}

pemakaian

    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);
    }

Berhati-hatilah karena longFunctionComplete TIDAK akan dijalankan pada thread yang sama seperti starthework.

Untuk metode yang mengambil parameter, Anda selalu dapat menggunakan closure, atau memperluas kelas.


3
Tidak jelas untuk semua orang ... stuffReturned ?, resultOfWork, PrintWorkComplete? dll.
Lost_In_Library

14

Berikut adalah contoh sederhana menggunakan delegasi ...

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();
  }
}

Ada seri hebat tentang threading di Threading di C # .


9

Cukup gunakan pendekatan delegasi.

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

Sekarang buat fungsi Multiply yang akan berfungsi di utas lain:

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

3
Mengapa jawaban di bawah ini "simpan ke file teks dan ambil kembali"?
Jon

7

Saya menemukan utas ini ketika juga mencoba mendapatkan nilai kembali dari metode yang dijalankan dalam Thread. Saya pikir saya akan memposting solusi saya yang berhasil.

Solusi ini menggunakan kelas untuk menyimpan metode yang akan dijalankan (secara tidak langsung) dan menyimpan nilai yang dikembalikan. Kelas dapat digunakan untuk fungsi apa pun dan jenis pengembalian apa pun. Anda cukup membuat instance objek menggunakan tipe nilai kembalian dan kemudian meneruskan fungsi untuk dipanggil melalui lambda (atau delegasi).


Implementasi 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();
    }
}

Untuk menggunakan kode ini, Anda dapat menggunakan Lambda (atau delegasi). Berikut contoh penggunaan lambda:

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...
}

Implementasi VB.NET 2008


Siapa pun yang menggunakan VB.NET 2008 tidak dapat menggunakan lambda dengan metode pengembalian non-nilai. Ini memengaruhi ThreadedMethodkelas, jadi kami akan ExecuteMethodmengembalikan nilai fungsinya. Ini tidak menyakiti apapun.

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

Dengan .NET Framework terbaru, dimungkinkan untuk mengembalikan nilai dari utas terpisah menggunakan Tugas, di mana properti Hasil memblokir utas panggilan hingga tugas selesai:

  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;

Untuk detailnya, silakan lihat http://msdn.microsoft.com/en-us/library/dd537613(v=vs.110).aspx


5

Utas ThreadStart di C # yang digunakan untuk memulai utas memiliki tipe kembalian 'void'.

Jika Anda ingin mendapatkan 'nilai kembali' dari utas, Anda harus menulis ke lokasi bersama (dengan cara yang aman untuk utas) dan membacanya setelah utas selesai dijalankan.


5

Jika Anda tidak ingin menggunakan BackgroundWorker, dan hanya menggunakan Thread biasa, Anda dapat mengaktifkan peristiwa untuk mengembalikan data seperti ini:

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

    }
}

Saya memperbaiki detail kecil di kode Anda. Sepertinya Anda meninggalkan thread1_bagian di kabel AsyncCompletedEventHandler miliknya . Jika hasil edit saya salah, bantu saya untuk memahami apa yang terjadi di sana.
jp2code

1
@ jp2code Anda tidak dapat melakukan thread1_Thread1Completed +=karena thread1_Thread1Completedadalah nama suatu fungsi, jadi Anda tidak dapat meletakkannya di sisi kiri operator penugasan. Sisi kiri Thread1Completed +=digunakan karena itu adalah event, sehingga bisa muncul di sisi kiri operator penugasan untuk menambahkan pengendali event. Lihatpublic event AsyncCompletedEventHandler Thread1Completed;
AaronLS

Saya melihatnya sekarang. Saya tidak tahu mengapa saya tidak dapat melihat pengendali kejadian di #regionbagian Anda sebelumnya. Aku melihat. Jujur! :)
jp2code

2

Untaian tidak benar-benar memiliki nilai balik. Namun, jika Anda membuat delegasi, Anda dapat memanggilnya secara asinkron melalui BeginInvokemetode ini. Ini akan menjalankan metode pada thread pool thread. Anda bisa mendapatkan nilai pengembalian apa pun dari seperti panggilan via EndInvoke.

Contoh:

static int GetAnswer() {
   return 42;
}

...

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

GetAnswerakan dieksekusi pada utas kumpulan utas dan ketika selesai Anda dapat mengambil jawabannya melalui EndInvokeseperti yang ditunjukkan.


2

The BackgroundWorker bagus ketika mengembangkan untuk Windows Forms.

Katakanlah Anda ingin lulus kelas sederhana bolak-balik:

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; }
}

Saya menulis kelas pendek yang melakukan hal berikut:

  • Buat atau Hapus daftar
  • Mulailah satu putaran
  • Dalam putaran, buat item baru untuk daftar
  • Dalam lingkaran, buat utas
  • Dalam putaran, kirim item sebagai parameter ke utas
  • Dalam putaran, mulai utas
  • Dalam putaran, tambahkan utas ke daftar untuk ditonton
  • Setelah loop, gabungkan setiap utas
  • Setelah semua penggabungan selesai, tampilkan hasilnya

Dari dalam rutinitas utas:

  • Kunci panggilan sehingga hanya 1 utas yang dapat memasuki rutinitas ini dalam satu waktu (yang lain harus menunggu)
  • Posting informasi tentang item tersebut.
  • Ubah item.
  • Saat utas selesai, data ditampilkan di konsol.

Menambahkan delegasi dapat berguna untuk memposting data Anda langsung kembali ke utas utama, tetapi Anda mungkin perlu menggunakan Panggil jika beberapa item data tidak aman untuk utas.

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);
        }
    }

}

Untuk mengujinya, buat Aplikasi Konsol kecil, dan letakkan ini di file 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);
        }
    }

}

Berikut tangkapan layar dari apa yang saya dapatkan dengan ini:

Output Konsol

Saya berharap orang lain bisa mengerti apa yang saya coba jelaskan.

Saya menikmati mengerjakan thread dan menggunakan delegasi. Mereka membuat C # sangat menyenangkan.

Lampiran: Untuk VB Coders

Saya ingin melihat apa saja yang terlibat dalam penulisan kode di atas sebagai Aplikasi Konsol VB. Konversi melibatkan beberapa hal yang tidak saya harapkan, jadi saya akan memperbarui utas ini di sini bagi mereka yang ingin tahu cara memasang utas di 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";
    }
}

Contoh yang diberikan salah, Anda beruntung itu berhasil untuk Anda. Bayangkan situasi berikut ini test(){ Thread.Sleep(5000); /*Highly time demanding process*/ return "Returned from test()";}. Dalam hal ini utas mandiri tidak akan punya waktu untuk menetapkan nilai baru ke returnValuevariabel. Sebagai upaya terakhir, Anda dapat menyimpan referensi utas var standaloneThread = new Thread(()=> //...);dan setelah itu, memulainya dengan cara yang disinkronkan standaloneThread.Start(); standaloneThread.Join();. Tapi ini jelas bukan praktik terbaik.
AlexMelw

1

Solusi sederhana adalah melewatkan parameter oleh ref ke fungsi yang berjalan di utas dan mengubah nilainya di utas.

       // 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
        }

Jika Anda tidak dapat mengubah fungsi yang berjalan di tapak, Anda dapat membungkusnya dengan fungsi lain:

 bool theirFunction(var someVar){
   return false;
}


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

tolong jelaskan downvote-nya. Saya menggunakan pola ini dalam kode saya sendiri dan bekerja dengan sangat baik.
CodeToad

0

Dapat menggunakan Kode Ini:

 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

Saya bukan ahli dalam threading, itulah mengapa saya melakukannya seperti ini:

Saya membuat file Pengaturan dan

Di dalam utas baru:

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

Kemudian saya mengambil nilai itu kapan pun saya membutuhkannya.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.