gpt4 book ai didi

.net - 发出信号并立即关闭 ManualResetEvent 是否安全?

转载 作者:行者123 更新时间:2023-12-04 02:08:43 26 4
gpt4 key购买 nike

我觉得我应该知道这个问题的答案,但我还是会问,以防万一我犯了一个潜在的灾难性错误。

以下代码按预期执行,没有错误/异常:

static void Main(string[] args)
{
ManualResetEvent flag = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(s =>
{
flag.WaitOne();
Console.WriteLine("Work Item 1 Executed");
});
ThreadPool.QueueUserWorkItem(s =>
{
flag.WaitOne();
Console.WriteLine("Work Item 2 Executed");
});
Thread.Sleep(1000);
flag.Set();
flag.Close();
Console.WriteLine("Finished");
}

当然,正如多线程代码通常的情况一样,成功的测试并不能证明这实际上是线程安全的。如果我输入 Close 测试也会成功之前 Set ,即使文档明确指出尝试在 Close 之后做任何事情将导致未定义的行为。

我的问题是,当我调用 ManualResetEvent.Set 时方法,是否保证信号 全部 在将控制权返回给调用者之前等待线程?换句话说,假设我能够保证不会再有对 WaitOne 的调用。 ,在这里关闭句柄是否安全,或者在某些情况下此代码是否可能会阻止某些服务员收到信号或导致 ObjectDisposedException ?

文档只说 Set将其置于“有信号状态” - 它似乎没有说明服务员何时会真正收到该信号,所以我想确定一下。

最佳答案

当您用 ManualResetEvent.Set 发出信号时您可以保证在将控制权返回给调用者之前,所有等待该事件的线程(即在 flag.WaitOne 上处于阻塞状态)都将收到信号。

当然,在某些情况下,您可能会设置标志并且您的线程看不到它,因为它在检查标志之前正在做一些工作(或者如果您正在创建多个线程,则作为 nobugs 建议):

ThreadPool.QueueUserWorkItem(s =>
{
QueryDB();
flag.WaitOne();
Console.WriteLine("Work Item 1 Executed");
});

标志上存在争用,现在您可以在关闭它时创建未定义的行为。您的标志是线程之间的共享资源,您应该创建一个倒计时闩锁,每个线程在完成时都会发出信号。这将消除对您的 flag 的争用.
public class CountdownLatch
{
private int m_remain;
private EventWaitHandle m_event;

public CountdownLatch(int count)
{
Reset(count);
}

public void Reset(int count)
{
if (count < 0)
throw new ArgumentOutOfRangeException();
m_remain = count;
m_event = new ManualResetEvent(false);
if (m_remain == 0)
{
m_event.Set();
}
}

public void Signal()
{
// The last thread to signal also sets the event.
if (Interlocked.Decrement(ref m_remain) == 0)
m_event.Set();
}

public void Wait()
{
m_event.WaitOne();
}
}
  • 每个线程在倒计时锁存器上发出信号。
  • 您的主线程在倒计时闩锁上等待。
  • 主线程在倒计时锁存信号后进行清理。

  • 最终,你最后 sleep 的时间并不是解决问题的安全方式,相反,你应该设计你的程序,使其在多线程环境中 100% 安全。

    更新:单生产者/多消费者
    这里的假设是您的生产者知道将创建多少消费者,在您创建所有消费者后,您重置 CountdownLatch给定数量的消费者:
    // In the Producer
    ManualResetEvent flag = new ManualResetEvent(false);
    CountdownLatch countdown = new CountdownLatch(0);
    int numConsumers = 0;
    while(hasMoreWork)
    {
    Consumer consumer = new Consumer(coutndown, flag);
    // Create a new thread with each consumer
    numConsumers++;
    }
    countdown.Reset(numConsumers);
    flag.Set();
    countdown.Wait();// your producer waits for all of the consumers to finish
    flag.Close();// cleanup

    关于.net - 发出信号并立即关闭 ManualResetEvent 是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2336767/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com