C#多线程 - 出了啥问题,如何使用 AutoResetEvent

Posted

技术标签:

【中文标题】C#多线程 - 出了啥问题,如何使用 AutoResetEvent【英文标题】:C# Multi-Threading - What's wrong, how to use AutoResetEventC#多线程 - 出了什么问题,如何使用 AutoResetEvent 【发布时间】:2020-07-29 00:58:38 【问题描述】:

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

    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 的状态并加入“需要处理”队列,这需要时间。 Main 的循环已在运行,无需等待信号。

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

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

如果我看到输出,那是我做错了一个适合书本的示例:

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

预期输出: 答:1 乙:2 C: 3 D: 4 E:5 女:6 克:7 高:8 我:9 J:10

有什么优雅的方法可以解决这个问题吗?也许使用锁等。

任何讨论将不胜感激。 谢谢!

【问题讨论】:

【参考方案1】:

有几个问题:

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

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

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

// 它是线程安全的,因为 FIFO?

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

【讨论】:

感谢 JonasH 的回答。如果我使用 task = Task.Factory.StartNew(WriteToConsole) 而不是 waitH.Set(),并更改 WriteToConsole() : lock (locker) ConsoleWriter(str.Dequeue (), num.Dequeue());代码有效。我将继续执行任务。

以上是关于C#多线程 - 出了啥问题,如何使用 AutoResetEvent的主要内容,如果未能解决你的问题,请参考以下文章

使用 discord.py 时在控制台中没有收到错误消息,我如何查看出了啥问题?

核心数据多线程——我做错了啥

在我的第一个视图 Django 中如何/出了啥问题

使用 Scipy 进行线性回归曲线拟合 - 不知道出了啥问题

C# WinForm TPL 任务调用意外结果-我在这里做错了啥? [复制]

我的 IF 语句出了啥问题并且是数字