从线程返回一个值?

如何从线程返回值?

从线程获取返回值的最简单方法之一是使用闭包。 创build一个variables来保存线程的返回值,然后将其捕获到一个lambdaexpression式中。 从工作者线程分配“返回”值给这个variables,然后一旦线程结束,你可以从父线程使用它。

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 } 

我将使用BackgroundWorker方法并在e.Result中返回结果。

编辑:

这通常与WinForms和WPF相关联,但可以由任何types的.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> ,并检查Task<T>.Result

一个线程不是一个方法 – 你通常不会“返回”一个值。

但是,如果您想从某些处理的结果中取回一个值,则有许多选项,主要有两个选项:

  • 您可以同步共享的一段数据,并进行适当的设置。
  • 您也可以通过某种forms的callback来传递数据。

这实际上取决于你如何创build线程,如何使用它,以及你正在使用的语言/框架/工具。

这是一个简单的例子,使用委托…

 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#中线程的线程有一个非常棒的系列。

这取决于你如何创build线程和可用的.NET版本:

.NET 2.0+:

A)你可以直接创buildThread对象。 在这种情况下,您可以使用“闭包” – 声明variables并使用lambdaexpression式捕获它:

 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类。 在这种情况下,您可以使用捕获的variables(如使用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和Task类来启动你的线程。 generics类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; }); 

注意:包含上面代码的方法应该用async关键字标记。

由于许多原因使用任务并行库是使用线程的首选方式。

我最喜欢的类,只用两行代码在另一个线程上运行任何方法。

 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的同一线程上执行。

对于使用参数的方法,你总是可以使用闭包,或者扩展这个类。

我也遇到这个线程时,也试图获得在线程中执行的方法的返回值。 我想我会发布我的解决scheme。

该解决scheme使用一个类来存储要执行的方法(间接)并存储返回值。 该类可以用于任何函数和任何返回types。 你只是使用返回值types实例化对象,然后传递函数通过lambda(或委托)进行调用。


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的人都不能使用具有非值返回方法的lambdaexpression式。 这会影响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 

使用最新的.NET Framework,可以使用Task从单独的线程返回值,其中Result属性将阻止调用线程,直到任务完成:

  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

如果你不想使用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 } } 

用于启动线程的C#中的ThreadStart委托具有返回types“void”。

如果您希望从线程获取“返回值”,则应该写入共享位置(以适当的线程安全方式),并在线程完成执行时读取该值。

线程并没有真正的返回值。 但是,如果您创build委托,则可以通过BeginInvoke方法asynchronous调用它。 这将在线程池线程上执行该方法。 你可以通过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检索答案,如图所示。

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

一个简单的解决scheme是通过ref传递参数到线程中运行的函数,并更改线程中的值。

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

为Windows窗体开发时, BackgroundWorker非常好。

假设你想来回传递一个简单的类:

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

我写了一个短小的课,内容如下:

  • 创build或清除列表
  • 开始一个循环
  • 在循环中,为列表创build一个新项目
  • 在循环中,创build一个线程
  • 在循环中,将该项目作为参数发送到线程
  • 在循环中,启动线程
  • 在循环中,添加线程列表来观看
  • 循环后,join每个线程
  • 所有连接完成后,显示结果

从线程例程中:

  • 调用locking,以便一次只有一个线程可以进入此例程(其他人必须等待)
  • 发布有关该项目的信息。
  • 修改项目。
  • 线程完成后,数据显示在控制台上。

添加一个委托对于将数据直接发回到主线程是有用的,但如果某些数据项不是线程安全的,则可能需要使用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); } } } 

要testing这个,创build一个小的控制台应用程序,并把它放在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编码器

我想看看上面的代码作为一个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 

只需使用委托方法。

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

现在使乘法function,将在另一个线程上工作:

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

我不是线程专家,所以我这样做:

我创build了一个设置文件和

在新的线程里面:

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

然后,只要我需要,我就拿起那个价值。

帕特里克