首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >控制AutoResetEvent信号

控制AutoResetEvent信号
EN

Stack Overflow用户
提问于 2015-12-10 14:00:26
回答 1查看 442关注 0票数 1

我有10个线程运行,我想控制信号,这样我就可以知道什么时候有5个线程向我发出信号,并能够阻止他们按需发送信号。

这是代码:

代码语言:javascript
复制
   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. 怎样才能在主循环结束后继续计算调用Set的次数呢?我得在锁区里数数?如果是这样的话,我如何向线程发送ref变量才能到达inc?

代码语言:javascript
复制
while (timesSetIsCalled < 5)
{
    are.WaitOne();
    timesSetIsCalled++;
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-12-11 01:24:08

  1. 怎样才能在主循环结束后继续计算调用Set的次数呢?我得在锁区里数数?如果是这样的话,我如何向线程发送ref变量才能到达inc?

只有调用Set()的线程才能计数调用Set()的线程。您无法从设置事件对象的线程外部观察到有多少线程设置了事件。

正如所发生的那样,解决这个问题需要将计数行为移到线程中,这样做也解决了您在退出while循环之后仍会继续进行计数的次要问题。

下面是您的代码的一个版本,它展示了您如何做到这一点:

代码语言:javascript
复制
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();
    }
}

备注:

  • 上面的关键是新的Counter类。它抽象了跟踪线程进程所需的计数器,以及用于同步线程的AutoResetEventInterlocked用于增量;该事件被简单地设置为向主线程发出至少一个计数器的更改。这样,无论增加多少次或多少计数器,主线程总是能够准确地知道发生了多少次。
  • 上面的代码还修复了新版本的代码中的一个微妙的错误。您已经将Random实例移出线程方法本身,而在A类的所有实例之间共享它。这比以前要好,但仍然不完全正确:Random类不是线程安全的,而是依赖于内部状态来产生正确的结果。同时使用它会导致非随机的,或者至少是不正确的随机输出.我在这里通过让主线程创建和使用唯一的Random对象来解决这个问题,在线程对象真正开始运行之前,根据需要将值传递给它们。
  • 我还修改了代码的基本结构。我希望这不是太令人困惑,而是提供了一些关于如何利用C#语言特性来提高代码可读性的见解。
  • 不需要使用DateTime.Now.Ticks.GetHashCode()作为Random实例的种子。Random类已经默认使用基于系统时钟的种子。

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

如果您的第二个问题是按照上述内容处理的,这是否仍然是必需的?看来可能不是。

但如果是的话,请张贴一个新的问题,明确你的意思。线程之间可以通过多种不同的方式进行通信,从您的问题中可以看出,您是否真的想阻止其他线程调用Set(),或者您是否只想延迟它们,在任何一种情况下,什么样的特定条件才能控制这种情况的发生。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34203822

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档