从线程返回值?

如何从线程返回值?

226192 次浏览

我将使用 背景工作者方法并返回结果 e。结果。

编辑:

这通常与 WinForms 和 WPF 相关联,但是可以由任何类型的。NET 应用程序。下面是使用 BackoundWorker 的控制台应用程序的示例代码:

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,并检查 Task<T>.Result

C # 中用于启动线程的 ThreadStart 委托返回类型为‘ void’。

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

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

但是,如果您试图从某些处理的结果中获取一个值,那么您有许多选项,其中两个主要选项是:

  • 您可以同步共享的数据片段,并对其进行适当的设置。
  • 还可以通过某种形式的回调将数据传回。

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

线程实际上没有返回值。但是,如果创建委托,则可以通过 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检索答案,如图所示。

下面是一个使用委托的简单示例..。

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

如果您不想使用 BackoundWorker,而只是使用常规 Thread,那么您可以触发一个事件来返回数据,如下所示:

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


}
}

我最喜欢的类,只用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 相同的线程上执行。

对于接受参数的方法,始终可以使用闭包或展开类。

我在尝试获取在 Thread 中执行的方法的返回值时遇到了这个线程。我认为我应该发布我的解决方案。

此解决方案使用一个类来存储(间接地)要执行的方法和存储返回值。该类可用于任何函数和任何返回类型。您只需使用返回值类型实例化该对象,然后通过 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 (或者一个委托):

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

NET 2008的实现


任何使用 VB.NET2008的人都不能使用带有非值返回方法的 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

从线程获取返回值的最简单方法之一是使用闭包。创建一个保存线程返回值的变量,然后将其捕获到 lambda 表达式中。将“ return”值从辅助线程分配给这个变量,然后一旦该线程结束,您就可以从父线程使用它。

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

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

我创建了一个设置文件

新帖子内容:

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

每当我需要的时候,我都会选择那个值。

最新消息。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

一个简单的解决方案是通过引用将参数传递给在线程中运行的函数,并更改其在线程中的值。

       // 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 窗体开发时,背景工作者非常好。

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

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

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

  • 创建或清除列表
  • 开始循环
  • 在循环中,为列表创建一个新项
  • 在循环中,创建一个线程
  • 在循环中,将项作为参数发送给线程
  • 在循环中,启动线程
  • 在循环中,将线程添加到要监视的列表中
  • 循环之后,加入每个线程
  • 所有连接完成后,显示结果

从线程程序的内部:

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

添加 delegate对于将数据直接发送回主线程非常有用,但是如果某些数据项不是线程安全的,则可能需要使用 调用

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


}

要测试这一点,创建一个小控制台应用,并将其放入 程序文件:

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


}

下面是我得到的截图:

Console Output

我希望其他人能理解我试图解释的东西。

我喜欢使用线程和委托,它们让 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

这取决于您希望如何创建线程和可用的.NET 版本:

.NET 2.0 + :

A)可以直接创建 Thread对象。在这种情况下,您可以使用“ close”-Declaration 变量并使用 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类开始你的线程。泛型类 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关键字。

出于许多原因,使用任务并行库是处理线程的较好方式。

只需使用委托方法。

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

现在让 Multiplyfunction 运行在另一个线程上:

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

可使用此密码:

 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;
}
class Program
{
public static void ActionResultPrint(string i)
{
Console.WriteLine(i);
}


static void Main(string[] args)
{
var tl = new List<Thread>();


tl.Add(new Thread(() => Run(10, ActionResultPrint)));
tl.Add(new Thread(() => Run(20, ActionResultPrint)));
tl.ForEach(x => x.Start());
tl.ForEach(x => x.Join());
}


public static void Run(int j, Action<string> action)
{
string rvalue = string.Empty;


for (int i = 0; i <= j; i++)
{
Thread.Sleep(100);
rvalue = i.ToString();
Console.WriteLine(rvalue);
}
action($@"output {j}");
}
}

使用线程时,可以通过以下方式传递和返回值:

int value = -1;
Thread t1 = new Thread(() => { value = compute(a); });
t1.Start();
if(value!=-1)
{...}


public int compute(int[] a1)
{
//...code logic
return -1;
}