C# 多线程 - 怎么了,如何使用 AutoResetEvent

C# Multi-Threading - What's wrong, how to use AutoResetEvent

我还在学习线程,我在使用下面这段代码时遇到了问题。抱歉,如果这个问题之前出现过,我真的不明白为什么这段代码不起作用。 我简化了代码:

    static EventWaitHandle waitH;      // AutoResetEvent, wait for signal
    static bool whExit;                // signal to exit waiting
    static Queue<string> str;          // waiting line (example values)
    static Queue<int> num;             // 

    static void Main(string[] args)
    {
        waitH = new AutoResetEvent(false);    // initialize waiter
        str = new Queue<string>();
        num = new Queue<int>();

        Thread thr = new Thread(new ThreadStart(Waiter));    // waiting in another thread
        thr.Start();                                         // start the waiting thread

        for(short i = 0; i < 10; i++)
        {
            str.Enqueue(string.Format($"{(char)(i + 65)}"));    // add something to queue
            num.Enqueue(i);    // add a number to test incrementing
            waitH.Set();    // signal to start the "long processing"
        }
    }

    static void Waiter()
    {
        while(!whExit)
        {
            waitH.WaitOne();    // wait for signal

            WriteToConsole();    // start the long processing on another thread
        }
    }

    static void WriteToConsole()
    {
        // threadstart with parameters
        // action: void delegate
        // get 2 values from waiting line
        var f = new ParameterizedThreadStart(obj =>
           new Action<string, int>(ConsoleWriter)
           (str.Dequeue(), num.Dequeue()));          // it's thread safe, because FIFO?

        Thread thr = new Thread(f);
        thr.IsBackground = true;           // close thread when finished
        thr.Start();
    }

    // print to console
    static void ConsoleWriter(string s, int n)
    {
        Console.WriteLine(string.Format($"{s}: {++n}"));     // easy example
    }

它在 Main 的循环中停止。 我认为问题是: Thread.Start() 首先被调用,但它需要改变 Thread 的状态并加入 "need to be processed" queue ,这需要时间。 Main 的循环已经 运行ning 而不是等待信号。

我用双向信号解决了这个问题:在循环中 waitH.Set() 之后使用另一个暂停信号 AutoResetEvent (WaitOne) 并在完成 Console.WriteLine() 后发出信号。

我并不真正为这个解决方案感到自豪,因为如果我这样做,程序就会失去 "threadish"、并行或同步方法。 这是一个例子,我想 运行 同时在不同的线程上进行长时间的计算。

如果我看到了输出,那是我做错的书装示例:

输出: 答:1 乙:2 有时 乙:2 答:1

预期输出: 答:1 乙:2 中:3 D:4 电子:5 女:6 体重:7 身高:8 我:9 J: 10

有什么优雅的方法可以解决这个问题吗?可能要用锁等

如有任何讨论,我们将不胜感激。 谢谢!

有几个问题:

  1. main 方法从不等待工作线程完成,因此它可能会 运行 完成并在完成之前停止所有线程。这可以通过通知工作线程停止,然后使用 thread.Join() 等待它完成来解决。

  2. WriteToConsole 从每个列表中取出一项并将其打印到控制台。但是线程可能会在 main 方法中的循环完成后启动。因此,当线程启动时,将发出 autoReset 事件信号并处理一项。但在下一次迭代中,autoResetEvent 将不再发出信号,并且永远不会再次发出信号。这可以通过在事件发出信号后遍历队列中的所有项目来解决。

  3. 像您提到的那样在循环中使用双向信号实际上会序列化代码,从而消除使用线程的任何好处。
  4. 如果这是一个学习练习,我建议先花时间学习 Tasks, async/await, lock and Parallel.For。如果你能很好地掌握这些东西,你将比手动使用线程和重置事件更有效。

// it's thread safe, because FIFO?

没有。如果需要线程安全集合,请使用 concurrent collections