jjzjj

c# - ManualResetEventSlim : Calling . Set() 后跟 .Reset() 不会释放 *任何* 等待线程

coder 2024-05-25 原文

ManualResetEventSlim:调用 .Set() 后立即调用 .Reset() 不会释放任何等待线程

(注意:ManualResetEvent 也会发生这种情况,而不仅仅是 ManualResetEventSlim。)

我在发布和 Debug模式下都尝试了下面的代码。 我在四核处理器上运行的 Windows 7 64 位上使用 .Net 4 作为 32 位版本运行它。 我从 Visual Studio 2012 编译它(因此安装了 .Net 4.5)。

在我的系统上运行它时的输出是:

Waiting for 20 threads to start
Thread 1 started.
Thread 2 started.
Thread 3 started.
Thread 4 started.
Thread 0 started.
Thread 7 started.
Thread 6 started.
Thread 5 started.
Thread 8 started.
Thread 9 started.
Thread 10 started.
Thread 11 started.
Thread 12 started.
Thread 13 started.
Thread 14 started.
Thread 15 started.
Thread 16 started.
Thread 17 started.
Thread 18 started.
Thread 19 started.
Threads all started. Setting signal now.

0/20 threads received the signal.

所以设置然后立即重置事件并没有释放单个线程。如果您取消对 Thread.Sleep() 的注释,那么它们都会被释放。

这似乎有些出乎意料。

谁有解释吗?

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

namespace Demo
{
    public static class Program
    {
        private static void Main(string[] args)
        {
            _startCounter = new CountdownEvent(NUM_THREADS); // Used to count #started threads.

            for (int i = 0; i < NUM_THREADS; ++i)
            {
                int id = i;
                Task.Factory.StartNew(() => test(id));
            }

            Console.WriteLine("Waiting for " + NUM_THREADS + " threads to start");
            _startCounter.Wait(); // Wait for all the threads to have called _startCounter.Signal() 
            Thread.Sleep(100); // Just a little extra delay. Not really needed.
            Console.WriteLine("Threads all started. Setting signal now.");
            _signal.Set();
            // Thread.Sleep(50); // With no sleep at all, NO threads receive the signal.
            _signal.Reset();
            Thread.Sleep(1000);
            Console.WriteLine("\n{0}/{1} threads received the signal.\n\n", _signalledCount, NUM_THREADS);
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }

        private static void test(int id)
        {
            Console.WriteLine("Thread " + id + " started.");
            _startCounter.Signal();
            _signal.Wait();
            Interlocked.Increment(ref _signalledCount);
            Console.WriteLine("Task " + id + " received the signal.");
        }

        private const int NUM_THREADS = 20;

        private static readonly ManualResetEventSlim _signal = new ManualResetEventSlim();
        private static CountdownEvent _startCounter;
        private static int _signalledCount;
    }
}

注意:这个问题提出了类似的问题,但似乎没有答案(除了确认是的,这可能会发生)。

Issue with ManualResetEvent not releasing all waiting threads consistently


[编辑]

正如 Ian Griffiths 在下面指出的那样,答案是所使用的底层 Windows API 并非旨在支持这一点。

不幸的是the Microsoft documentation for ManualResetEventSlim.Set()错误地指出它

Sets the state of the event to signaled, which allows one or more threads waiting on the event to proceed.

显然“一个或多个”应该是“零个或多个”。

最佳答案

重置 ManualResetEvent 与调用 Monitor.Pulse 不同 - 它不保证会释放任何特定数量的线程。相反,文档(针对底层 Win32 同步原语)非常清楚,您无法知道会发生什么:

Any number of waiting threads, or threads that subsequently begin wait operations for the specified event object, can be released while the object's state is signaled

这里的关键词是“任何数字”,其中包括零。

Win32 确实提供了 PulseEvent,但正如它所说的那样“此功能不可靠,不应使用。” http://msdn.microsoft.com/en-us/library/windows/desktop/ms684914(v=vs.85).aspx 文档中的评论提供一些关于为什么不能用事件对象可靠地实现脉冲式语义的见解。 (基本上,内核有时会将正在等待事件的线程暂时从其等待列表中取出,因此线程总是有可能错过事件的“脉冲”。无论您使用 PulseEvent 都是如此或者您尝试通过设置和重置事件来自己完成。)

ManualResetEvent 的预期语义是它充当一个门。门在您设置时打开,在您重置时关闭。如果您打开一扇门,然后在任何人有机会通过大门之前迅速将其关闭,那么如果每个人仍然站在大门的错误一侧,您就不会感到惊讶。只有那些在你开着门的时候足够警觉的人才能通过。这就是它的工作方式,所以这就是为什么你看到你所看到的。

特别是 Set 的语义非常不是“打开门,并确保所有等待的线程都通过门”。 (如果是这个意思,那么内核应该如何处理多对象等待并不明显。)所以这不是一个“问题”,因为事件并不意味着按照你的方式使用尝试使用它,所以它运行正常。但从某种意义上说,这是一个问题,您将无法使用它来获得您正在寻找的效果。 (这是一个有用的原语,它只是对你想要做的事情没有用。我倾向于将 ManualResetEvent 专门用于最初关闭的门,并且只打开一次,永远不会关闭再次。)

因此您可能需要考虑其他一些同步原语。

关于c# - ManualResetEventSlim : Calling . Set() 后跟 .Reset() 不会释放 *任何* 等待线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15067369/

有关c# - ManualResetEventSlim : Calling . Set() 后跟 .Reset() 不会释放 *任何* 等待线程的更多相关文章

  1. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  2. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  3. ruby - Highline 询问方法不会使用同一行 - 2

    设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案

  4. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  5. ruby-on-rails - 项目升级后 Pow 不会更改 ruby​​ 版本 - 2

    我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby​​版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby​​版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘

  6. ruby-on-rails - link_to 不显示任何 rails - 2

    我试图在索引页中创建一个超链接,但它没有显示,也没有给出任何错误。这是我的index.html.erb代码。ListingarticlesTitleTextssss我检查了我的路线,我认为它们也没有问题。PrefixVerbURIPatternController#Actionwelcome_indexGET/welcome/index(.:format)welcome#indexarticlesGET/articles(.:format)articles#indexPOST/articles(.:format)articles#createnew_articleGET/article

  7. ruby-on-rails - RSpec:避免使用允许接收的任何实例 - 2

    我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_

  8. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  9. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  10. ruby - Sinatra set cache_control to static files in public folder编译错误 - 2

    我不知道为什么,但是当我设置这个设置时它无法编译设置:static_cache_control,[:public,:max_age=>300]这是我得到的syntaxerror,unexpectedtASSOC,expecting']'(SyntaxError)set:static_cache_control,[:public,:max_age=>300]^我只想将“过期”header设置为css、javaascript和图像文件。谢谢。 最佳答案 我猜您使用的是Ruby1.8.7。Sinatra文档中显示的语法似乎是在Ruby1.

随机推荐