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

Roj*_*Gh. 5 .net c# task manualresetevent .net-core

我正在尝试使用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();
    }
}
Run Code Online (Sandbox Code Playgroud)

大多数情况下,代码运行良好并输出:

Hello from main thread! 00:00:01.1017591
Hello from task! 00:00:01.1017630
Run Code Online (Sandbox Code Playgroud)

但是,每五六次运行一次代码,我只会得到以下输出:

Hello from main thread! 00:00:01.1017591
Run Code Online (Sandbox Code Playgroud)

WaitTask 内部之后的代码永远不会被调用。

我使用.NET Core 2.0.2Windows Server 2012 R2Visual Studio 15.4.1

任何人都可以重现这种行为吗?

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

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

当我在调试/发布配置中的命令行中构建和运行代码时,似乎没有问题。

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

结果:在重新启动 IDE、清理项目并重新构建项目后,到目前为止我还没有遇到这个问题。我怀疑 IDE 中存在小错误。但是我不能报告它,因为它现在已经消失了。

如果其他人遇到此问题并希望跟踪和报告错误,我将保留此问题。

Arh*_*ief 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();
}
Run Code Online (Sandbox Code Playgroud)

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

对 Set() 的一次调用会发出事件信号,并且所有等待的任务都会被释放。在调用 Reset() 方法之前,对 Wait() 的新调用不会阻塞。

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

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();
}
Run Code Online (Sandbox Code Playgroud)

你会发现一切都按照你的预期进行。