控制AutoResetEvent的信号

时间:2015-12-10 14:00:26

标签: c# multithreading autoresetevent

我有10个线程正在运行,我想控制信号,以便我可以判断5个线程何时发出信号,并且能够阻止它们按需发信号。

这是代码:

   class A
    {
        static readonly Random r = new Random(DateTime.Now.Ticks.GetHashCode());
        static int ID = 1;
        int id;

        public A()    {   id = ID++;   }

        public void SomeFunc(object o)
        {
            Thread.Sleep(r.Next(2000, 5000));
            lock (o)
            {
                Console.WriteLine("A-{0} called Set!", id);
                ((EventWaitHandle)o).Set();
            }
        }

        static void Main(string[] args)
        {
            AutoResetEvent are = new AutoResetEvent(false);
            A[] arr = new A[10];

            for (int i = 0; i < arr.Length; i++)
                arr[i] = new A();

            for (int i = 0; i < arr.Length; i++)
                new Thread(arr[i].SomeFunc).Start(are);

            int timesSetIsCalled = 0;
            while (timesSetIsCalled < 5)
            {
                are.WaitOne();
                timesSetIsCalled++;
            }

            Console.WriteLine("{0} threads called set", timesSetIsCalled);
            Thread.Sleep(7000);
            Console.WriteLine("{0} threads called set", timesSetIsCalled);
        }
    }

考虑N个线程已经调用了Set函数 我的问题是:

  1. 如何阻止其他线程(已经运行)调用Set?

  2. 如何在主要的while循环之后继续计算调用Set的踏板?我必须算在锁定区域内?如果是这样,我怎样才能将一个ref变量发送给线程以便包含?

  3. while (timesSetIsCalled < 5)
    {
        are.WaitOne();
        timesSetIsCalled++;
    }
    

1 个答案:

答案 0 :(得分:1)

  
      
  1. 如何在主要的while循环之后继续计算调用Set的踏板?我必须算在锁定区域内?如果是这样,我怎样才能将一个ref变量发送到线程以便inc?
  2.   

只有调用System.MissingMethodException was unhandled Message: An unhandled exception of type 'System.MissingMethodException' occurred in NewSol1.exe Additional information: Method not found: 'Microsoft.FSharp.Collections.FSharpList`1<System.String> MyLib.Extensions.Extension1.ext1Method1(System.String)'. 的线程才能计算调用Set()的线程。您无法通过在设置它的线程之外观察事件对象来判断有多少个线程设置事件。

实际上,解决这个问题需要将计数行为转移到线程中,这样做也可以解决您在退出Set()循环后继续进行此计数的次要问题。

以下是代码的一个版本,说明了如何执行此操作:

while

备注:

  • 上面的关键是新的class Program { static void Main(string[] args) { Random r = new Random(); WaitCounter counter = new WaitCounter(); A[] arr = new A[10]; for (int i = 0; i < arr.Length; i++) arr[i] = new A(r.Next(2000, 5000), counter); for (int i = 0; i < arr.Length; i++) new Thread(arr[i].SomeFunc).Start(); while (counter.Counter < 5) { counter.Wait(); } Console.WriteLine("{0} threads called set", counter.Counter); Thread.Sleep(7000); Console.WriteLine("{0} threads called set", counter.Counter); } } class A { private static int ID = 1; private static readonly Stopwatch sw = Stopwatch.StartNew(); private readonly int id; private readonly int duration; private readonly WaitCounter counter; public A(int duration, WaitCounter counter) { id = ID++; this.duration = duration; this.counter = counter; } public void SomeFunc(object o) { Thread.Sleep(duration); counter.Increment(); Console.WriteLine( @"{0}-{1} incremented counter! Elapsed time: {2:mm\:ss\.fff}", GetType().Name, id, sw.Elapsed); } } class WaitCounter { private readonly AutoResetEvent _event = new AutoResetEvent(false); private int _counter; public int Counter { get { return _counter; } } public void Increment() { Interlocked.Increment(ref _counter); _event.Set(); } public void Wait() { _event.WaitOne(); } } 类。这抽象了跟踪线程所需的计数器。进度,以及用于同步线程的CounterAutoResetEvent用于递增;该事件被设置为简单地向主线程发信号通知已经对至少一个计数器进行了改变。通过这种方式,增加计数器的次数或数量并不重要,主线程将始终能够确切地确定发生了多少次。
  • 上面还修复了新版代码中的一个细微错误。您已将Interlocked实例移出线程方法本身,而是在Random类的所有实例之间共享它。这比以前更好,但仍然不太正确:A类不是线程安全的,并且依赖于内部状态来产生正确的结果。同时使用它可能导致非随机或至少不正确的随机输出。我通过让主线程创建并使用唯一的Random对象来解决这个问题,在实际开始运行之前,根据需要将值传递给线程对象。
  • 我还对代码的基本结构进行了更改。我希望这不会太迷惑,而是提供一些见解,了解如何利用C#语言功能使代码更具可读性。
  • 无需使用Random作为DateTime.Now.Ticks.GetHashCode()实例的种子。 Random类已默认使用基于系统时钟的种子。
  
      
  1. 如何阻止其他线程(已经运行)调用Set?
  2.   

如果您的第二个问题按上述方式解决,是否还需要这个?似乎可能不是。

但如果是的话,请发一个新问题,明确你的意思。线程可以通过很多不同的方式相互通信,并且从您的问题中不清楚您是否确实希望阻止其他线程调用Random或者你只想延迟它们,在任何一种情况下,特定的条件将控制是否发生这种情况。