在.net中进行线程化的计时器

0 .net c# multithreading timer

我想要一个新线程内的计时器tick/elapsed事件.好像我不能使用Windows计时器.但是如果我使用Timers.Timer,它会为每个已发生的事件从线程池创建工作线程.有没有办法让这些事件发生在同一个线程中?

更新:

谢谢大家回答这个问题.

尽管如此,我在整个事情背后的意图可能听起来有些疯狂.我要你纠正我.当我试图解决这个问题时,这是我的想法(作为新手).我正在尝试每2秒执行一次任务.当我使用Timers.Timer时,它每2秒创建一个线程,我认为这是一个开销.

我的主线程和我的其他线程需要很多处理器的时间来执行他们的任务.因此,如果我可以避免创建这些线程,我将保存处理器的任何微秒,以便在每次计时器过去时为我的主线程和其他线程创建一个线程.

我进行了快速测试并比较了几个解决方案.每种情况下间隔1000毫秒.100个蜱虫.

解决方案1:等待/休眠的无限循环{00:01:42.0068344}

解决方案2:使用Brian的Synchronizer {00:01:42.4068573}

解决方案3:Timers.Timer,因为它是{00:01:42.4018571}

这应该告诉我2.0068344,2.4068573,2.4018571是在背景中浪费其他东西的时间,而不是100个刻度的1000毫秒的时间间隔.这应该意味着当solution1满足您的需求时,它是性能最佳的解决方案?

这也意味着虽然Brian的解决方案与一个线程同步,但它实际上是在后台创建线程.

请确认或纠正我.

Bri*_*eon 6

是.您可以在同一个线程中System.Timers.Timer执行Elapsed事件处理程序.要执行此操作,您需要将SynchronizingObject属性设置为一个对象,该对象将执行封送到您选择的线程上.但是,重要的是要意识到您不能只是将执行注入随机线程.接收线程必须专门设计为允许编组方法调用的执行.

public static void Main(string[] args)
{
    var timer = new System.Timers.Timer();
    timer.Interval = 1000;
    timer.Elapsed += 
      (s, a) => 
      { 
        Console.WriteLine("{1} {0}", 
          DateTime.Now, Thread.CurrentThread.ManagedThreadId); 
      };
    timer.SynchronizingObject = new Synchronizer();
    timer.Start();
    Console.ReadLine();
}
Run Code Online (Sandbox Code Playgroud)

这是Synchronizer该类的代码.此代码使用.NET BCL 4.0中的BlockingCollection类.如果你没有使用4.0,那么你可以用Stephen Toub的BlockingQueue替换它.

public class Synchronizer : ISynchronizeInvoke
{
    private Thread m_Thread;
    private BlockingCollection<Message> m_Queue = new BlockingCollection<Message>();

    public Synchronizer()
    {
        m_Thread = new Thread(Run);
        m_Thread.IsBackground = true;
        m_Thread.Start();
    }

    private void Run()
    {
        while (true)
        {
            Message message = m_Queue.Take();
            message.Return = message.Method.DynamicInvoke(message.Args);
            message.Finished.Set();
        }
    }

    public IAsyncResult BeginInvoke(Delegate method, object[] args)
    {
        Message message = new Message();
        message.Method = method;
        message.Args = args;
        m_Queue.Add(message);
        return message;
    }

    public object EndInvoke(IAsyncResult result)
    {
        Message message = result as Message;
        if (message != null)
        {
            message.Finished.WaitOne();
            return message.Return;
        }
        throw new ArgumentException("result");
    }

    public object Invoke(Delegate method, object[] args)
    {
        Message message = new Message();
        message.Method = method;
        message.Args = args;
        m_Queue.Add(message);
        message.Finished.WaitOne();
        return message.Return;
    }

    public bool InvokeRequired
    {
        get { return Thread.CurrentThread != m_Thread; }
    }

    private class Message : IAsyncResult
    {
        public Delegate Method = null;
        public object[] Args = null;
        public object Return = null;
        public object State = null;
        public ManualResetEvent Finished = new ManualResetEvent(false);

        public object AsyncState
        {
            get { return State; }
        }

        public WaitHandle AsyncWaitHandle
        {
            get { return Finished; }
        }

        public bool CompletedSynchronously
        {
            get { return false; }
        }

        public bool IsCompleted
        {
            get { return Finished.WaitOne(0); }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:

你需要了解它的System.Timers.Timer工作原理.它实际上System.Threading.Timer在幕后使用并在ThreadPool线程上执行事件处理程序.所以它根本不是真正创建任何线程.来自线程池的相同内容会一次又一次地被回收.没有线程创建开销.这是线程池的重点.

现在,我在这里提供的解决方案仍然使用ThreadPool幕后,但不是让工作项Elapsed直接执行事件处理程序,而是执行将事件处理程序Synchronizer.BeginInvoke编组到Synchronizer创建的线程上的方法.

因此,我在这里提供的解决方案会更慢,这应该是不可忽视的.就个人而言,我会坚持使用解决方案#1,因为它更容易.在这一点上,我不会担心很多关于性能的问题.