我希望在未来的某个时刻启动计时器以执行一次。我想使用lambda表达式来简化代码。所以我想做点像......
(new System.Threading.Timer(() => { DoSomething(); },
null, // no state required
TimeSpan.FromSeconds(x), // Do it in x seconds
TimeSpan.FromMilliseconds(-1)); // don't repeat
我觉得它很整洁。但在这种情况下,不会丢弃Timer对象。解决这个问题的最佳方法是什么?或者,我应该在这里做一个完全不同的方法吗?
答案 0 :(得分:5)
这将完成你想要的,但我不确定它是最好的解决方案。我觉得它的东西短而优雅,但可能比它的价值更令人困惑和难以理解。
System.Threading.Timer timer = null;
timer = new System.Threading.Timer(
(object state) => { DoSomething(); timer.Dispose(); }
, null // no state required
,TimeSpan.FromSeconds(x) // Do it in x seconds
,TimeSpan.FromMilliseconds(-1)); // don't repeat
答案 1 :(得分:3)
这种做法存在缺陷 您正在内存中创建一个对象而没有引用它。这意味着计时器对象可用于垃圾回收。虽然此代码在某些时候会起作用,但您无法预测垃圾收集何时启动并删除计时器。
例如,在下面的代码中,我强制进行垃圾收集,这会导致计时器永远不会触发。
static void Main(string[] args)
{
DoThing();
GC.Collect();
Thread.Sleep(5000);
}
static void DoThing()
{
new System.Threading.Timer(x => { Console.WriteLine("Here"); },
null,
TimeSpan.FromSeconds(1),
TimeSpan.FromMilliseconds(-1));
}
答案 2 :(得分:1)
你可以包装计时器类......
class Program
{
static void Main(string[] args)
{
MyTimer.Create(
() => { Console.WriteLine("hello"); },
5000);
GC.Collect();
GC.WaitForPendingFinalizers();
Console.Read();
}
}
public class MyTimer
{
private MyTimer() { }
private Timer _timer;
private ManualResetEvent _mre;
public static void Create(Action action, int dueTime)
{
var timer = new MyTimer();
timer._mre = new ManualResetEvent(false);
timer._timer = new Timer(
(x) =>
{
action();
timer._mre.Set();
},
null,
dueTime,
Timeout.Infinite
);
new Thread(new ThreadStart(() =>
{
timer._mre.WaitOne();
timer._timer.Dispose();
})).Start();
}
}
答案 3 :(得分:1)
而不是使用计时器,而是利用线程池:
bool fired = false;
ThreadPool.RegisterWaitForSingleObject(new ManualResetEvent(false),
(state, triggered) =>
{
fired = true;
},
0, 9000, true);
GC.Collect();
Thread.Sleep(10000);
Assert.IsTrue(fired);
这可以保留垃圾收集,因为您不必保留对任何内容的引用。
答案 4 :(得分:0)
计时器对象可能实现析构函数。 您可以在文档或反射器中轻松验证。
如果这是真的,你不应该担心。除非这段代码被多次调用,否则你应该努力确定性地重新分配定时器,这意味着你将拥有一组定时器,例如。
答案 5 :(得分:0)
如果你有一个Dispatcher并希望进入UI(Dispatcher)线程,请使用:
void MyNonAsyncFunction()
{
Dispatcher.InvokeAsync(async () =>
{
await Task.Delay(1000);
MessageBox.Show("Thank you for waiting");
});
}
此功能不是异步的,因为您不想在功能中等待。如果您想在不同时间安排多个事件,这种方法可能很有用,但您可能真的想要以下方法:
async void MyAsyncFunction()
{
// Do my other things
await Task.Delay(1000);
MessageBox.Show("Thank you for waiting");
}
它做同样的事情,但需要等待在你的函数结束时发生。
由于您可能没有Dispatcher或想要使用它,但仍希望在不同时间安排多个操作,我会使用一个线程:
static void MyFunction()
{
// Do other things...
Schedule(1000, delegate
{
System.Diagnostics.Debug.WriteLine("Thanks for waiting");
});
}
static void Schedule(int delayMs, Action action)
{
#if DONT_USE_THREADPOOL
// If use of threadpool is undesired:
new System.Threading.Thread(async () =>
{
await Task.Delay(delayMs);
action();
}
).Start(); // No need to store the thread object, just fire and forget
#else
// Using the threadpool:
Task.Run(async delegate
{
await Task.Delay(delayMs);
action();
});
#endif
}
如果您想避免异步,我建议不要使用线程池并用Task.Delay(delayMs)
调用替换等待Thread.Sleep(delayMs)
来电
答案 6 :(得分:-2)
System.Reactive.Linq.Observable.Interval(TimeSpan.FromSeconds(1))
.FirstAsync()
.Subscribe(_ => DoSomething()));