AutoResetEvent过程?

发布于 2024-11-25 03:30:28 字数 876 浏览 2 评论 0原文

private ConcurrentQueue<Data> _queue = new ConcurrentQueue<Data>();
private AutoResetEvent _queueNotifier = new AutoResetEvent(false);

public void MoreData(Data example)
{
    _queue.Enqueue(example);
    _queueNotifier.Set();
}

private void _SimpleThreadWorker()
{
    while (_socket.Connected)
    {
        _queueNotifier.WaitOne();
        Data data;
        if (_queue.TryDequeue(out data))
        {
            //handle the data
        }
    }
}

一旦事件出队,我是否必须将其设置为 false,或者当事件返回 _queueNotifier.WaitOne() 时,它会自行返回 false 或者它是如何工作的?

我应该像下面的示例一样使用内部 while 还是两种方式都很好/相等?

while (_socket.Connected)
{
    _queueNotifier.WaitOne();
    while (!_queue.IsEmpty)
    {
        Data data;
        if (_queue.TryDequeue(out data))
        {
            //handle the data
        }
    }
}
private ConcurrentQueue<Data> _queue = new ConcurrentQueue<Data>();
private AutoResetEvent _queueNotifier = new AutoResetEvent(false);

public void MoreData(Data example)
{
    _queue.Enqueue(example);
    _queueNotifier.Set();
}

private void _SimpleThreadWorker()
{
    while (_socket.Connected)
    {
        _queueNotifier.WaitOne();
        Data data;
        if (_queue.TryDequeue(out data))
        {
            //handle the data
        }
    }
}

Do I have to set the event to false once I have it Dequeue or the event goes back to false on it's own when it hits back _queueNotifier.WaitOne() or how it works exactly ?

Should I use a inner while like the below example instead or both ways are just fine/equal ?

while (_socket.Connected)
{
    _queueNotifier.WaitOne();
    while (!_queue.IsEmpty)
    {
        Data data;
        if (_queue.TryDequeue(out data))
        {
            //handle the data
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

哽咽笑 2024-12-02 03:30:28

如果您使用 .NET 4 中的 ConcurrentQueue,最好避免完全由 AutoResetEvent 自行处理。相反,创建一个 BlockingCollection 来包装ConcurrentQueue 并使用它 - 它可以完成您需要的一切。 (如果您只是使用无参数构造函数创建一个 BlockingCollection ,它无论如何都会为您创建一个 ConcurrentQueue 。)

编辑:如果您确实想仍然使用 AutoResetEvent,然后 WaitOne 将自动(并且原子地)重置事件 - 这是 AutoResetEvent 的“自动”部分。将此与 ManualResetEvent 进行比较,后者不会重置事件。

If you're using ConcurrentQueue from .NET 4, it's best to avoid doing the AutoResetEvent handling yourself entirely. Instead, create a BlockingCollection to wrap the ConcurrentQueue and just use that - it does everything you need. (If you just create a BlockingCollection using the parameterless constructor, it'll create a ConcurrentQueue for you anyway.)

EDIT: If you really want to still use AutoResetEvent, then WaitOne will automatically (and atomically) reset the event - that's the "Auto" part of AutoResetEvent. Compare this with ManualResetEvent which doesn't reset the event.

尹雨沫 2024-12-02 03:30:28

当您执行 _queueNotifier.Set() 时,事件将发出信号。当事件发出信号并且从另一个线程调用 _queueNotifier.WaitOne() 时,会同时发生两件事(即在内核模式下):

  • 事件变为未发出信号(因为它是自动重置)
  • 调用的线程WaitOne 已解锁,

因此您不必自己显式设置事件状态。

然而,正如 Jon 所说,如果您对共享变量所做的唯一事情就是从队列中推送和拉出项目,那么简单地使用 BlockingCollection 会更方便。

如果您正在访问多个共享变量,那么围绕它使用单线程同步机制(您自己的)可能是有意义的。

另外,在我看来,如果您打算使用自己的代码,最好这样做:

public void MoreData(Data example)
{
    var queueWasEmpty = _queue.IsEmpty;
    _queue.Enqueue(example);
    if (queueWasEmpty) {
        _queueNotifier.Set();
    }
}

private void _SimpleThreadWorker()
{
    while (_socket.Connected)
    {
        _queueNotifier.WaitOne();
        Data data;
        while(!queue.IsEmpty) {
            if (_queue.TryDequeue(out data))
            {
                //handle the data
            }
        }
    }
}

这样,每当项目添加到当消费者功能繁忙时排队。您还可以避免在消费者函数的每次迭代中进入内核模式。

确实,对于后者,避免上下文切换是以添加 _queue.IsEmpty 测试为代价的(其中糟糕的实现无论如何都可能会进行上下文切换);但是,该操作可能是作为互锁比较交换操作实现的,不需要进入内核模式。

免责声明:我没有检查源代码或IL来验证上述信息。

When you do _queueNotifier.Set() the event becomes signaled. When the event is signaled and _queueNotifier.WaitOne() is called from the other thread, two things happen simultaneously (i.e. in kernel mode):

  • The event becomes unsignaled (since it's auto-reset)
  • The thread calling WaitOne is unblocked

So you don't have to explicitly set the event status yourself.

However, as Jon says, if the only thing you are doing with your shared variables is to push and pull items from the queue, simply using a BlockingCollection is more convenient.

If you are accessing multiple shared variables then it might make sense to have a single thread sync mechanism (your own) around that.

Also, it seems to me that if you are going to use your own code it would be better to do something like this:

public void MoreData(Data example)
{
    var queueWasEmpty = _queue.IsEmpty;
    _queue.Enqueue(example);
    if (queueWasEmpty) {
        _queueNotifier.Set();
    }
}

private void _SimpleThreadWorker()
{
    while (_socket.Connected)
    {
        _queueNotifier.WaitOne();
        Data data;
        while(!queue.IsEmpty) {
            if (_queue.TryDequeue(out data))
            {
                //handle the data
            }
        }
    }
}

This way you won't have to go into kernel mode (to set the event) whenever items get added to the queue while the consumer function is busy. You also avoid going into kernel mode in each iteration of the consumer function.

It's true that regarding the latter, avoiding the context switch comes at the cost of adding the _queue.IsEmpty test (wherein a bad implementation might do a context switch anyway); however, that one is probably implemented as an interlocked compare exchange operation which doesn't require going into kernel mode.

Disclaimer: I have not checked source code or IL to verify the above information.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文