ManualResetEventSlim.Set()并不总是解锁任务中的等待

时间:2017-10-23 09:49:16

标签: c# .net .net-core task manualresetevent

我正在尝试使用ManualResetEventSlim类在几个并行任务之间进行通信。

以下是代码的简化版本:

class Program
{
    private static void Main(string[] args)
    {
        Stopwatch stopwatch = Stopwatch.StartNew();

        ManualResetEventSlim eventSlim = new ManualResetEventSlim(false);

        Task.Run(() =>
        {
            Task.Run(() =>
                            {
                                Thread.Sleep(1000);

                                eventSlim.Set();
                            });

            eventSlim.Wait();

            Console.WriteLine($"Hello from the Task! {stopwatch.Elapsed}");
        });

        eventSlim.Wait();

        Console.WriteLine($"Hello from the Main thread! {stopwatch.Elapsed}");

        stopwatch.Stop();

        Console.ReadLine();
    }
}

大多数情况下代码运行正常并输出:

Hello from main thread! 00:00:01.1017591
Hello from task! 00:00:01.1017630

然而,每五到六次我运行代码时,我只得到这个输出:

Hello from main thread! 00:00:01.1017591

任务中的Wait之后的代码永远不会被调用。

我在.NET Core 2.0.2 Windows Server 2012 R2上使用Visual Studio 15.4.1

有人可以重现这种行为吗?

任何人都可以确认我的代码是否正确或是否有任何问题吗?

更新 在@ArhiChief建议在Release配置中测试结果之后,我发现只有在我使用IDE调试代码时才会出现问题。

当我在Debug / Release配置中的命令行中构建和运行代码时,似乎没有任何问题。

我尝试关闭/重新打开IDE并清理项目并进行全新的重建,现在IDE似乎也正常工作。

结果:在重新启动IDE,清理项目和重新完成项目之后,到目前为止我还没有遇到过这个问题。我怀疑IDE中的一个小错误。但是我无法报告,因为它现在已经消失了。

如果其他人遇到此问题并希望跟踪并报告该错误,我会将此问题保持打开状态。

1 个答案:

答案 0 :(得分:0)

我也知道您的首要任务可能会被处理和收集。 考虑一下这段代码

static void Main(string[] args)
{
Stopwatch stopwatch = Stopwatch.StartNew();

ManualResetEventSlim eventSlim = new ManualResetEventSlim(false);

Task.Run(() =>
{
    Task.Run(() =>
    {
        Thread.Sleep(1000);

        eventSlim.Set();
    });

    eventSlim.Wait();

    Console.WriteLine($"Hello from the Task! {stopwatch.Elapsed}");
});

eventSlim.Wait();

Console.WriteLine($"Hello from the Main thread! {stopwatch.Elapsed}");

stopwatch.Stop();

//Console.ReadLine();
}

我收到消息Hello from the Main thread! 00:00:01.0111623。我也读过那个

  

对Set()的单次调用会向事件发出信号,并释放所有等待的任务。对Wait()的新调用不会阻塞,直到调用Reset()方法。

但是让我们回到我们的代码。如果你像这样重写它

static void Main(string[] args)
{
Stopwatch stopwatch = Stopwatch.StartNew();

ManualResetEventSlim eventSlim = new ManualResetEventSlim(false);

var t = Task.Run(() =>
{
    Task.Run(() =>
    {
        Thread.Sleep(1000);

        eventSlim.Set();
    });

    eventSlim.Wait();

    Console.WriteLine($"Hello from the Task! {stopwatch.Elapsed}");
});

eventSlim.Wait();

Console.WriteLine($"Hello from the Main thread! {stopwatch.Elapsed}");

stopwatch.Stop();

t.Wait();
//Console.ReadLine();
}

你会发现一切都按预期运作。