0

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

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

提前致谢。

4

4 回答 4

3

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

为每个线程使用单独的局部变量,您在线程构造函数中分配该变量,类似于您刚才使用response.

然后等待所有线程使用.Join()

t1.Join();
t2.Join();
t3.Join();

response然后在所有Join()调用返回后使用局部变量进行设置。

但是,我会改用任务。这是一个例子。它在不同的线程中运行三个不同的方法,每个方法都有不同的返回类型:

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.

于 2013-10-01T12:09:55.303 回答
0

你可以这样做:

// 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);
于 2013-10-01T12:18:20.697 回答
0

发生这种情况是因为线程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);
    }
于 2013-10-01T12:13:00.883 回答
0

问题可能就是这样,t2并且t3取决于t1完成。如果t2t3在此之前完成,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);

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

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

于 2013-10-01T14:33:47.127 回答