【问题标题】:Pass System.Threading.Timer object reference to its callback function将 System.Threading.Timer 对象引用传递给其回调函数
【发布时间】:2014-04-02 10:40:53
【问题描述】:

是否可以将 System.Threading.Timer 对象引用传递给其回调函数,如下所示:

System.Threading.Timer myTimer = new System.Threading.Timer(new TimerCallback(DoSomething), myTimer, 2000, Timeout.Infinite);

因为我想在“DoSomething”方法中调用:

myTimer.Change(5000, Timeout.Infinite);

我将在下面粘贴一个草稿控制台应用程序。 想法是这样的:我有计时器列表。每个计时器都会发出一些请求,当它收到请求时,它会更改一些共享数据。 但是,我不能将计时器的引用传递给它的回调,也不能使用它的索引,因为它由于某种原因变成了“-1”(调查中)..

using System;
using System.Collections.Generic;
using System.Threading;

namespace TimersInThreads
{
    class Program
    {
        public static int sharedDataInt;
        static private readonly object lockObject = new object();
        public static List<System.Threading.Timer> timers = new List<Timer>();

        static void Main(string[] args)
        {
            System.Threading.Timer timer = new System.Threading.Timer(new TimerCallback(DoSomething), timers.Count - 1, 2000, Timeout.Infinite);
            timers.Add(timer);

            System.Threading.Timer timer2 = new System.Threading.Timer(new TimerCallback(DoSomething), timers.Count - 1, 2000, Timeout.Infinite);
            timers.Add(timer2);

            System.Threading.Timer timer3 = new System.Threading.Timer(new TimerCallback(DoSomething), timers.Count - 1, 2000, Timeout.Infinite);
            timers.Add(timer3);

            //timer = new System.Threading.Timer(new TimerCallback(DoSomething), "Timer 1", 1000, Timeout.Infinite);
            //timer = new System.Threading.Timer(new TimerCallback(DoSomething), "Timer 2", 450, Timeout.Infinite);
            //timer = new System.Threading.Timer(new TimerCallback(DoSomething), "Timer 3", 1500, Timeout.Infinite);

            Console.ReadLine();

        }

        static void DoSomething(object timerIndex)
        {
            // Request
            // Get Response
            var x = getSomeNumberWithDelay();


            // Executes after Response is received
            lock (lockObject)
            {
                sharedDataInt++;
                Console.WriteLine("Timer" + (int)timerIndex + ", SHaredDataInt: " + sharedDataInt + "\t\t" + DateTime.Now.ToString("HH:mm:ss tt") + "." + DateTime.Now.Millisecond.ToString());
            }

            timers[(int)timerIndex].Change(5000, Timeout.Infinite);
        }

        static int getSomeNumberWithDelay()
        {
            Thread.Sleep(5000);
            return 3;
        }
    }
}

请给我一些想法或建议。 非常感谢,谢谢!

【问题讨论】:

    标签: c# .net timer


    【解决方案1】:

    Timer constructor 中的state 参数作为参数传递给TimerCallback - 这只是一个object,因此可以处理任何事情,包括计时器引用本身。

    所以

    new System.Threading.Timer(new TimerCallback(DoSomething), myTimer, 2000, Timeout.Infinite);
    

    完全可以接受。在您的回调中,您只需要将参数转换为 Timer 例如

    static void DoSomething(object state)
    {
        ...
        var timer = (System.Threading.Timer)state;
    }
    

    再次查看您的问题,我看到您正在尝试将计时器作为参数传递给它的构造函数(这显然无法完成,因为它尚未正式声明然而)。您可以通过将计时器显式传递给回调来解决此问题,例如

    Timer t = null;
    t = new Timer(delegate { DoSomething(t); }, null, 2000, Timeout.Infinite);
    

    在触发回调时,t 将设置为计时器的引用。

    【讨论】:

    • 我考虑过并尝试过,但它说“使用未分配的局部变量'timer'”。所以,我实际上做不到。
    • 嘿,很聪明。谢谢你的想法!
    【解决方案2】:

    将每次出现的timers.Count - 1 替换为timers.Count

    System.Threading.Timer timer = new System.Threading.Timer(new TimerCallback(DoSomething), timers.Count, 2000, Timeout.Infinite);
    timers.Add(timer);
    

    当您将第一个计时器添加到列表中时,那里没有元素,因此 timers.Count 等于 0

    另一种方法是将特定计时器的实例作为回调的第二个参数而不是其索引传递。

    【讨论】:

    • 哦...我的...上帝...谢谢。公认。 惭愧
    • oleksii,是的,这就是我想要的。但德米特里的建议会有所帮助。计时器存储在列表中,所以通过它的索引我就知道它叫什么计时器。
    【解决方案3】:

    我为此开设了一堂课。它提供了 TypedCallback、State 和对 Timer 的引用。该类维护一个引用列表并提供 Disposal。

    我没有足够的“积分”来制作 cmets,但也想指出,在 James 的回答中,您需要确保保留对 Timer 的引用。正如文档所述,即使它们仍在运行,它们也可能成为垃圾。

    /// <summary>
    /// Holds a list of references to Timers; and provides typed objects to reference the Timer and its
    /// State from within a TypedCallback. Synchronized. Usage:
    /// <code>
    /// TypedStateTimers myTimers = new TypedStateTimers(3);
    /// public void MyMethod() {
    ///     typedStateTimers.Create("Hello, from Timer", new TypedCallback<string>((sr) => {
    ///         System.Console.WriteLine(sr.State); // "Hello, from Timer"
    ///         sr.Dispose(); // Invoke the StateRef method to ensure references are released
    ///     })).Start(1500, Timeout.Infinite);
    /// }
    /// </code>
    /// </summary>
    public class TypedStateTimers
    {
        /// <summary>
        /// A typed delegate used as the callback when Timers are created.
        /// </summary>
        public delegate void TypedCallback<T>(StateRef<T> state);
    
    
        /// <summary>
        /// Wraps a Timer and State object to be used from within a TypedCallback.
        /// </summary>
        public class StateRef<T> : IDisposable
        {
            /// <summary>The state passed into TypedStateTimers.Create. May be null.</summary>
            public T State { get; internal set; }
    
            /// <summary>The Timer: initially not started. Not null.</summary>
            public System.Threading.Timer Timer { get; internal set; }
    
            /// <summary>The TypedStateTimers instance that created this object. Not null.</summary>
            public TypedStateTimers Parent { get; internal set; }
    
    
            /// <summary>
            /// A reference to this object is retained; and then Timer's dueTime and period are changed
            /// to the arguments.
            /// </summary>
            public void Start(int dueTime, int period) {
                lock (Parent.Treelock) {
                    Parent.Add(this);
                    Timer.Change(dueTime, period);
                }
            }
    
            /// <summary>Disposes the Timer; and releases references to Timer, State, and this object.</summary>
            public void Dispose() {
                lock (Parent.Treelock) {
                    if (Timer == null) return;
                    Timer.Dispose();
                    Timer = null;
                    State = default(T);
                    Parent.Remove(this);
                }
            }
        }
    
    
        internal readonly object Treelock = new object();
        private readonly List<IDisposable> stateRefs;
    
    
        /// <summary>
        /// Constructs an instance with an internal List of StateRef references set to the initialCapacity.
        /// </summary>
        public TypedStateTimers(int initialCapacity) {
            stateRefs = new List<IDisposable>(initialCapacity);
        }
    
    
        /// <summary>Invoked by the StateRef to add it to our List. Not Synchronized.</summary>
        internal void Add<T>(StateRef<T> stateRef) {
            stateRefs.Add(stateRef);
        }
    
        /// <summary>Invoked by the StateRef to remove it from our List. Not synchronized.</summary>
        internal void Remove<T>(StateRef<T> stateRef) {
            stateRefs.Remove(stateRef);
        }
    
        /// <summary>
        /// Creates a new StateRef object containing state and a new Timer that will use the callback and state.
        /// The Timer will initially not be started. The returned object will be passed into the callback as its
        /// argument. Start the Timer by invoking StateRef.Start. Dispose the Timer, and release references to it,
        /// state, and the StateRef by invoking StateRef.Dispose. No references are held on the Timer, state or
        /// the StateRef until StateRef.Start is invoked. See the class documentation for a usage example.
        /// </summary>
        public StateRef<T> Create<T>(T state, TypedCallback<T> callback) {
            StateRef<T> stateRef = new StateRef<T>();
            stateRef.Parent = this;
            stateRef.State = state;
            stateRef.Timer = new System.Threading.Timer(
                    new TimerCallback((s) => { callback.Invoke((StateRef<T>) s); }),
                    stateRef, Timeout.Infinite, Timeout.Infinite);
            return stateRef;
        }
    
        /// <summary>Disposes all current StateRef instances; and releases all references.</summary>
        public void DisposeAll() {
            lock (Treelock) {
                IDisposable[] refs = stateRefs.ToArray();
                foreach (IDisposable stateRef in refs) stateRef.Dispose();
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-07-01
      • 2012-09-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多