如何在 c# 中将返回值附加到 3 个线程中的单个复杂变量
本文关键字:线程 复杂 单个 变量 返回值 | 更新日期: 2023-09-27 18:33:31
我在 3 个线程中调用 3 个不同的函数,我需要附加来自 3 个线程的所有返回值。
我尝试的是
Thread t1 = new Thread(() => response.Candidate = AddCandidate2Daxtra(request, args));
t1.Start();
Thread t2 = new Thread(() => response.Candidate.HRXML = parsecv(profile));
t2.Start();
Thread t3 = new Thread(() => response.Candidate.Attachments.Add(Print2Flash(alias, bytes, args)));
t3.Start();
while (t1.IsAlive == true || t2.IsAlive == true || t3.IsAlive == true)
{
Thread.Sleep(1000);
}
但最后我只得到第一个线程值。我没有获得剩余的两个线程值。所以任何人请帮我什么问题?
提前谢谢。
您有争用条件,因为您不知道线程的完成顺序。
为每个线程使用一个单独的局部变量,您可以在线程构造函数中分配该变量,类似于您刚才对 response
所做的那样。
然后使用 .Join()
等待所有线程:
t1.Join();
t2.Join();
t3.Join();
然后使用局部变量在所有Join()
调用返回后设置response
。
但是,我会改用任务。下面是一个示例。它在单独的线程中运行三种不同的方法,每种方法都有不同的返回类型:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Demo
{
class Program
{
private void run()
{
// Using tasks directly:
var task1 = Task<int>.Factory.StartNew(methodOne);
var task2 = Task<string>.Factory.StartNew(methodTwo);
var task3 = Task<double>.Factory.StartNew(methodThree);
// Alternatively:
// var task1 = Task.Run(new Func<int>(methodOne));
// var task2 = Task.Run(new Func<string>(methodTwo));
// var task3 = Task.Run(new Func<double>(methodThree));
string result = string.Format
(
"Task 1: {0}, Task 2: {1}, Task 3: {2}",
task1.Result, // Accessing Task.Result automatically
task2.Result, // waits for the task to complete.
task3.Result
);
Console.WriteLine(result);
// Alternatively, you can use tasks indirectly via Parallel.Invoke().
// You might find this more readable and less typing:
int r1 = 0;
string r2 = null;
double r3 = 0;
Parallel.Invoke
(
() => r1 = methodOne(),
() => r2 = methodTwo(),
() => r3 = methodThree()
);
result = string.Format
(
"Task 1: {0}, Task 2: {1}, Task 3: {2}",
r1,
r2,
r3
);
Console.WriteLine(result);
}
static int methodOne()
{
Thread.Sleep(1000);
return 1;
}
static string methodTwo()
{
Thread.Sleep(750);
return "two";
}
static double methodThree()
{
Thread.Sleep(500);
return 3.0;
}
static void Main(string[] args)
{
new Program().run();
}
}
}
无论您采用哪种方法,重要的是您不应该将结果直接分配给线程或任务内部的response
- 等到所有线程或任务都完成,然后才将结果分配给response
。
发生这种情况是因为线程t1
是在t2
和t3
之后调度的。您可以像这样重写代码:
Thread t1 = new Thread(() => response.Candidate = AddCandidate2Daxtra(request, args));
t1.Start();
t1.Join();
Thread t2 = new Thread(() => response.Candidate.HRXML = parsecv(profile));
t2.Start();
Thread t3 = new Thread(() => response.Candidate.Attachments.Add(Print2Flash(alias, bytes, args)));
t3.Start();
while (/*t1.IsAlive == true || */t2.IsAlive == true || t3.IsAlive == true)
{
Thread.Sleep(1000);
}
你可以这样做:
// get all the data you need separately
Candidate candidate = null; // no idea of the real type
Thread t1 = new Thread(() => candidate = AddCandidate2Daxtra(request, args));
t1.Start();
HRXML HRXML = null; // no idea of the real type
Thread t2 = new Thread(() => HRXML = parsecv(profile));
t2.Start();
Attachment att = null; // no idea of the real type
Thread t3 = new Thread(() => att = Print2Flash(alias, bytes, args));
t3.Start();
while (t1.IsAlive || t2.IsAlive || t3.IsAlive)
{
Thread.Sleep(1000);
}
当一切都完成后:
response.Candidate = candidate;
candidate.HRXML = HRXML;
response.Candidate.Attachments.Add(att);
问题可能是t2
和t3
依赖于t1
完成。如果t2
和t3
在t1
之前完成,则他们的结果将被注入到较旧的Candidate
实例中。您需要打破这种依赖关系。
请考虑以下代码。
var t2 = Task.Factory.StartNew(() => parsecv(profile));
var t3 = Task.Factory.StartNew(() => Print2Flash(alias, bytes, args));
response.Candidate = AddCandidate2Daxtra(request, args);
response.Candidate.HRXML = t2.Result;
response.Candidate.Attachments.Add(t3.Result);
我们在这里所做的是异步启动t2
和t3
,当它们完成后,它们的返回值将存储在创建的Task
实例中。同时,最初t1
的内容现在将在主线程上同步执行。创建一个新线程来完成这项工作是没有意义的。只需让主线程执行此操作,否则无论如何它都会闲置。一旦AddCandidate2Daxtra
完成,就可以从Task.Result
中检索其构成值并分配。Task.Result
属性将阻止,直到任务完成。
还有其他各种方法可以做到这一点,但大多数方法不会像上面那样优雅和易于阅读。如果您无法使用Task
请在评论中告诉我,我将更新我的答案以包含非 TPL 友好的方法。