如何在 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);
    }

但最后我只得到第一个线程值。我没有获得剩余的两个线程值。所以任何人请帮我什么问题?

提前谢谢。

如何在 c# 中将返回值附加到 3 个线程中的单个复杂变量

您有争用条件,因为您不知道线程的完成顺序。

为每个线程使用一个单独的局部变量,您可以在线程构造函数中分配该变量,类似于您刚才对 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是在t2t3之后调度的。您可以像这样重写代码:

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

问题可能是t2t3依赖于t1完成。如果t2t3t1之前完成,则他们的结果将被注入到较旧的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);

我们在这里所做的是异步启动t2t3,当它们完成后,它们的返回值将存储在创建的Task实例中。同时,最初t1的内容现在将在主线程上同步执行。创建一个新线程来完成这项工作是没有意义的。只需让主线程执行此操作,否则无论如何它都会闲置。一旦AddCandidate2Daxtra完成,就可以从Task.Result中检索其构成值并分配。Task.Result属性将阻止,直到任务完成。

还有其他各种方法可以做到这一点,但大多数方法不会像上面那样优雅和易于阅读。如果您无法使用Task请在评论中告诉我,我将更新我的答案以包含非 TPL 友好的方法。