在调用N秒方法A后,我使用以下代码调用方法B.如果方法A 在N秒超时内再次调用,我必须重置计数回N秒的时间。
我无法在我的项目中引用System.Windows.Form,因此我无法使用System.Windows.Form.Timer。
必须在同一个线程A中调用方法B。
private void InitTimer()
{
timer = new BackgroundWorker();
timer.WorkerSupportsCancellation = true;
timer.WorkerReportsProgress = true;
timer.DoWork += delegate(object sender, DoWorkEventArgs e)
{
var st = DateTime.Now;
while (DateTime.Now.Subtract(st).TotalSeconds < 10)
{
if (timer.CancellationPending)
{
e.Cancel = true;
return;
}
}
};
timer.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
{
if (!e.Cancelled)
{
MethodB();
}
else
{
timer.RunWorkerAsync();
}
};
}
public void MethodA()
{
if (timer.IsBusy)
timer.CancelAsync();
else
timer.RunWorkerAsync();
}
public void MethodB()
{
//do some stuff
}
实际上代码工作,但我认为这有点令人困惑。你知道是否有最佳实践来达到同样的效果吗?
答案 0 :(得分:4)
遗憾的是你被困在.NET 2.0上,因为Rx extensions有Throttle
方法可以非常优雅地实现这种效果。
可悲的是,Rx至少需要.NET 3.5 SP1。
哦,好吧!您可以随时使用System.Threading.Timer
来完成此操作。可以通过利用当前SynchronizationContext
来提供同步(这是BackgroundWorker
所做的。)
这是一个LaggedMethodPair
类的草图来说明这种方法。该类在其构造函数中接受三个输入:一个Action
按需执行,另一个Action
作为将在给定超时时间后调用的回调,当然,超时本身:
public sealed class LaggedMethodPair
{
private SynchronizationContext _context;
private Timer _timer;
private Action _primaryAction;
private Action _laggedCallback;
private int _millisecondsLag;
public LaggedMethodPair(Action primaryAction,
Action laggedCallback,
int millisecondsLag)
{
if (millisecondsLag < 0)
{
throw new ArgumentOutOfRangeException("Lag cannot be negative.");
}
// Do nothing by default.
_primaryAction = primaryAction ?? new Action(() => { });
// Do nothing by default.
_laggedCallback = laggedCallback ?? new Action(() => { });
_millisecondsLag = millisecondsLag;
_timer = new Timer(state => RunTimer());
}
public void Invoke()
{
// Technically there is a race condition here.
// It could be addressed, but in practice it will
// generally not matter as long as Invoke is always
// being called from the same SynchronizationContext.
if (SynchronizationContext.Current == null)
{
SynchronizationContext.SetSynchronizationContext(
new SynchronizationContext()
);
}
_context = SynchronizationContext.Current;
ResetTimer();
_primaryAction();
}
void ResetTimer()
{
_timer.Change(_millisecondsLag, Timeout.Infinite);
}
void RunTimer()
{
_context.Post(state => _laggedCallback(), null);
}
}
我编写了一个示例Windows窗体应用程序来显示此类的实际操作。表单包含LaggedMethodPair
成员,超时为2000毫秒。其primaryAction
将一个项添加到列表视图中。其laggedCallback
将突出显示的项添加到列表视图中。
您可以看到代码按预期运行。
答案 1 :(得分:1)
我会将此功能封装到一个timer类中,其中包含其他类可以订阅的事件(例如timer.tick事件)。
答案 2 :(得分:0)
我正在尝试使用AutoResetEvent
,因为它能够等待信号。我用它让工人等待来自A()的信号,如果它已经太长,将调用B()。
class Caller
{
AutoResetEvent ev = new AutoResetEvent(false);
public void A()
{
ev.Set();
// do your stuff
Console.Out.WriteLine("A---");
}
void B()
{
Console.Out.WriteLine("B---");
}
public void Start()
{
var checker = new BackgroundWorker();
checker.DoWork += new DoWorkEventHandler(checker_DoWork);
checker.RunWorkerAsync();
}
void checker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while (!worker.CancellationPending)
{
bool called = ev.WaitOne(TimeSpan.FromSeconds(3));
if (!called) B();
}
}
}
我已经粗略地测试了我的课程,到目前为止工作正常。请注意,将从工作线程调用B,因此如果需要,您必须在B()中进行同步。