如何在线程之间传递数据?

时间:2016-06-19 22:31:10

标签: c# multithreading

我想将数据从一个线程移动到另一个线程,但我的代码仅适用于我传递的第一个值,而不是将前5个值保存到列表中,然后将其打印出来 这是我的代码:

 private readonly ConcurrentQueue<int> _queue = new ConcurrentQueue<int>();
    private readonly AutoResetEvent _signal = new AutoResetEvent(false);

    public void Thread1()
    {
        List<int> values = new List<int>();
        int lastInput;
        StringBuilder sb = new StringBuilder();

        while (values.Count < 5)
        {
            _signal.WaitOne();
            _queue.TryDequeue(out lastInput);

            values.Add(lastInput);
        }
        for (int i = 0; i < values.Count; i++)
        {
            sb.Append(String.Format("{0}\n", values[i]));
        }
        MessageBox.Show(sb.ToString());
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Thread th1 = new Thread(Thread1);
        th1.Start();

        for (int i = 0; i < 8; i++)
        {
            _queue.Enqueue(i);
            _signal.Set();
        }
    }

3 个答案:

答案 0 :(得分:3)

我看到你要做的事情,@ MarcGravell的评论是正确的,以及@mariosangiorgio所说的是真的。您可以做的解决方法是使用Monitor Wait/Pulse机制。请尝试以下方法:

    private readonly Queue<int> _queue = new Queue<int>();
    private readonly object _locker = new object();

    public void Thread1()
    {
        List<int> values = new List<int>();
        int lastInput;
        StringBuilder sb = new StringBuilder();

        while (values.Count < 5)
        {
            lock (this._locker)
            {
                // wait until there is something in the queue
                if (this._queue.Count == 0)
                {
                    Monitor.Wait(this._locker);
                }

                // get the item from the queue
                _queue.Dequeue(out lastInput);

                // add the item to the list
                values.Add(lastInput);
            }
        }

        for (int i = 0; i < values.Count; i++)
        {
            sb.Append(String.Format("{0}\n", values[i]));
        }
        MessageBox.Show(sb.ToString());
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Thread th1 = new Thread(Thread1);
        th1.Start();

        for (int i = 0; i < 8; i++)
        {
            lock (this._locker)
            {
                // put something in the queue
                _queue.Enqueue(i);

                // notify that there is something in the queue
                Monitor.Pulse(this._locker);
            }
        }
    }

所以,基本上你要做的就是调用一个循环来尝试总共消耗5个项目。如果消费者线程发现队列中没有要使用的项目,它将等待生产者将一些项目放入队列中。一旦生产者将项目放入队列,它将告诉等待的消费者线程它已准备就绪!然后,消费者线程将解除阻塞并消耗队列中可能存在的任何项目。

另外,如果你考虑@mariosangiorgio评论,你实际上是在使用并发集合。所以他是对的,实际上并不需要阻止。因此,如果您想进行自己的阻止/解除阻止实验,可以使用我的实现并使用常规Queue(非并发)。或者,就像@mariosangiorgio说的那样,只需删除AutoResetEvent并让ConcurrentQueue做其事。

尽管如此,请记住,如果你没有阻止,你将不断循环并运行CPU,直到实际得到Dequeue'd。

答案 1 :(得分:1)

我以为我会为这个问题提供不同的解决方案。您当然可以使用AutoResetEventConcurrentQueue,但这会使代码难以理解,正确并且有理由。

您通常应该尝试使用具有更简单抽象的库。我喜欢微软的Reactive Framework(NuGet&#34; Rx-Main&#34;,&#34; Rx-WinForms&#34;或者&#34; Rx-WPF&#34;)。

Rx允许您创建类似LINQ的操作管道,这些操作是异步执行的,您可以在其中指定您要使用的调度程序(线程)。

这相当于您的代码:

IDisposable subscription =
    Observable
        .Range(0, 5, Scheduler.Default)
        .ToArray()
        .Select(xs => String.Join(Environment.NewLine, xs))
        .ObserveOn(this)
        .Subscribe(x => MessageBox.Show(x));

使用Scheduler.Default将计算推送到Windows应用程序的新线程。所以字符串生成远离UI线程。 .ObserveOn(this)将计算推回到UI(因为this引用当前表单 - 您可以使用任何UI元素而不是this)。

subscription是一个IDisposable,因此您可以随时调用subscription.Dispose()来缩短计算时间,如果它长时间运行并且您希望它停止。

Rx库非常强大,它为您提供了许多操作符,可以以相对简单的形式执行一些非常复杂的计算。

答案 2 :(得分:0)

我认为问题在于_signal.Set();_signal.WaitOne();

只有当你在WaitOne之前总是调用Set的交错时,这才会按照你想要的方式工作。我怀疑在你的情况下你有以下事件交错:

_signal.WaitOne(); // This waits for the first set
_signal.Set(); // This notifies the first `WaitOne`
_signal.Set(); // This doesn't notify anything
_signal.Set(); // This doesn't notify anything
_signal.Set(); // This doesn't notify anything
_signal.Set(); // This doesn't notify anything
_signal.WaitOne(); // Nothing is going to set this

由于您使用的是ConcurrentQueue,因此您不需要使用AutoResetEvent。只需删除它,一切都应该有效。