简体   繁体   English

将System.Threading.Timer对象引用传递给其回调函数

[英]Pass System.Threading.Timer object reference to its callback function

is it possible to pass the System.Threading.Timer object reference to its callback function, something like this: 是否可以将System.Threading.Timer对象引用传递给它的回调函数,如下所示:

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

Because in "DoSomething" method I want to call: 因为在“DoSomething”方法中我想调用:

myTimer.Change(5000, Timeout.Infinite);

I'll paste a draft console application below. 我将在下面粘贴草稿控制台应用程序。 Idea is this: I have List of timers. 想法是这样的:我有计时器列表。 And every timer makes some request and when it receives it, it changes some shared data. 并且每个计时器都会发出一些请求,当它收到请求时,它会更改一些共享数据。 But, I can't pass the reference to timer into its callback, nor can I use it's index, because it becomes "-1" for some reason(investigating).. 但是,我无法将定时器的引用传递给它的回调,也不能使用它的索引,因为它因某种原因(调查)变为“-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;
        }
    }
}

Please, give me some idea or advice. 请给我一些想法或建议。 Much appreciated, thanks! 非常感谢,谢谢!

The state parameter in the Timer constructor is passed as an argument to the TimerCallback - this is just an object therefore will work with anything, including the timer reference itself. Timer构造函数中的state参数作为参数传递给TimerCallback - 这只是一个object因此可以处理任何事情,包括计时器引用本身。

So 所以

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

is perfectly acceptable. 完全可以接受。 In your callback you would just need to cast the parameter as Timer eg 在你的回调中你只需要将参数转换为Timer例如

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

Looking at your problem again, I see what you are trying to do is pass the timer as a parameter into it's constructor (which obviously can't be done as it hasn't been officially declared yet). 再看一下你的问题,我看到你要做的是将计时器作为参数传递给它的构造函数(显然这是不能完成的,因为它尚未正式声明)。 You can workaround this though by passing the timer to the callback explicitly eg 您可以通过将计时器显式传递给回调来解决此问题,例如

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

By the time the callback is triggered t will set to the reference of the timer. 当回调被触发时, t将设置为定时器的参考。

Replace each occurrence of timers.Count - 1 with timers.Count : 更换的每次出现timers.Count - 1timers.Count

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

When you adding first timer to the list, there is no elements there, so the timers.Count equals to 0 . 将第一个计时器添加到列表时,没有元素,因此timers.Count等于0

Alternative way is to pass an instance of particular timer as the second argument of the callback instead of its index. 另一种方法是将特定计时器的实例作为回调的第二个参数而不是其索引。

I have a class for this purpose. 我为此目的上了一堂课。 It provides a TypedCallback, with State, and a reference to the Timer. 它提供TypedCallback,State和对Timer的引用。 The class maintains a List of references and provides Disposal. 该类维护一个引用列表并提供Disposal。

I don't have enough "points" to make comments, but also wanted to point out that in James' answer, you need to be sure to retain a reference to the Timer. 我没有足够的“积分”来发表评论,但也想指出在James的回答中,你需要确保保留对Timer的引用。 As the documentation states, they can become garbage even if still running. 正如文档所述,即使仍在运行,它们也会变成垃圾。

/// <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();
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM