【问题标题】:Subscribe multiple threads to the same event signal to wake up多个线程订阅同一个事件信号唤醒
【发布时间】:2021-09-02 01:08:42
【问题描述】:

我有一个队列包装类,它将项目存储到一个列表中,主程序中的多个线程正在使用这些项目,直到列表为空。此时,线程必须等待,直到缓冲区将更多项目排入列表。

每当一个项目入队时,我都会触发一个事件,我需要通知线程他们有新的项目可供使用。

 public event EventHandler ItemEnqueued;

    public void Enqueue(string item)
    {
        _itemsList.Add(item);            
        OnItemEnqueued();
    }
    

    void OnItemEnqueued()
    {
        ItemEnqueued?.Invoke(this, EventArgs.Empty);
    }

如何通知主程序的线程一个项目已入队?

非常感谢!

编辑澄清

    public class Queue 
{
    private readonly List<string> _itemsList = new List<string>();

    public void Enqueue(String val)
    {
        _itemsList.Add(val);            
        OnItemEnqueued();
    }
    

    void OnItemEnqueued()
    {
        //here I have to tell the threads that a new item has been added
    }

    public string Dequeue()
    {
            //FIFO queue
            if (_itemsList.Any())
            {
                var first = _itemsList.First();
                _itemsList.RemoveAt(0);

                return first;
            }
            
            return default(string);
       
    }

    public int Count()
    {
        return _itemsList.Count;
    }
}

}

我必须创建两个线程并让它们为排队的项目“战斗”:

    class Program
{
    readonly object _syncLock = new object();
    Queue _q = new Queue();
    static void Main(string[] args)
    {
        Program p = new Program();
        p.InitThreads();
        
    }

    public void InitThreads()
    {
        lock (_syncLock) //exclusively add items 
        {
            for (var i = 0; i <= 99; i++)
            {
                _q.Enqueue(i.ToString());
            }
        }

        Thread t1 = new Thread(() => {
            while(_q.Count() > 0)
                Console.WriteLine("T1 dequeued " + Consume());

        });

        Thread t2 = new Thread(() =>
        {
            while (_q.Count() > 0)
                Console.WriteLine("T2 dequeued " + Consume());              
        });
        

        t1.Start();
        t2.Start();

        //at some moment I have to tell the threads to wait until a new item has been enqueued
    }

    public string Consume()
    {
        lock (_syncLock) //safely get one item
        {
            return _q.Dequeue(); 
        }
    }

}

【问题讨论】:

  • 听起来您最好使用BlockingCollection&lt;T&gt; 并让您的线程使用GetConsumingEnumerable() 进行迭代
  • 甚至还有 Channels、DataFlow、Rx 或许多其他东西。简而言之,这个问题已经解决了很多次,您可能会节省一些时间并找到更好的开箱即用解决方案
  • 重点是我必须以这种特定的方式解决它,它是练习语句......否则我会寻找另一个解决方案。
  • 那么解决方案将比仅仅向线程发出新项目已添加的信号更复杂 - 您还需要确保一次只有一个线程试图使项目出队。但是,您可以使用ManualResetEventAutoResetEvent 来表示已添加项目(取决于您希望所有线程还是仅一个线程继续)。然后,您的线程在执行任何操作之前等待发出信号的事件。需要更多的脚手架,但是您的问题有点含糊,无法发布明确的答案...
  • 我尝试使用 ManualResetEvent 或 AutoResetEvent 但我不知道如何,我已经阅读了很多页面和教程都没有成功。在主程序中,我确实锁定了实例,只让一个线程从队列中消费。但是一旦完成,我不知道如何对不同的线程说:嘿,新物品可用,争取获得它们。我可以从主程序 job.OnItemEnqueued 中看到,但我不知道如何将其链接到线程以使其唤醒。

标签: c# multithreading


【解决方案1】:

不确定这是否是您要查找的内容,但这里有一个可兼容的代码示例,演示了如何使用 AutoResetEvent 一次仅向一个线程发出信号:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    static class Program
    {
        public static void Main()
        {
            var signal = new AutoResetEvent(initialState:false);

            for (int i = 0; i < 4; ++i)
                Task.Run(() => worker(signal));

            while (true)
            {
                Console.WriteLine("Press <ENTER> to wake a thread.");
                Console.ReadLine();
                signal.Set();
            }
        }

        static void worker(AutoResetEvent signal)
        {
            int threadId = Thread.CurrentThread.ManagedThreadId;

            while (true)
            {
                Console.WriteLine($"Thread {threadId} is waiting for a signal.");
                signal.WaitOne();
                Console.WriteLine($"Thread {threadId} received a signal");
            }
        }
    }
}

要注意的关键是你创建了一个AutoResetEvent 并将它传递给所有线程,然后等待它。然后,当“主”线程希望其中一个线程响应时,它会发出事件信号。

我不会为这种事情使用ManualResetEvent,因为这样所有线程都会同时发出信号,看起来你想避免这种情况。

【讨论】:

  • 不完全是,我希望从队列类事件 (OnItemEnqueued) 中触发 autoresetevent。非常感谢
  • 那么您只需从OnItemEnqueued() 拨打signal.Set()
  • 对不起,伙计,我正在浪费你的时间,因为我自己解释得不好。我为此感到难过。我将完全重写并发布所有代码,这样可能会更容易理解我必须做什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-27
  • 1970-01-01
  • 2015-09-16
  • 1970-01-01
  • 2013-12-25
  • 2022-11-03
相关资源
最近更新 更多