[英]Call Method B if method A is not called for more than N seconds
我正在使用以下代碼在N秒后調用方法A來調用方法B。 如果在N秒的超時時間內再次調用方法A,我必須將計時重新設置為N秒。
我無法在項目中引用System.Windows.Form,因此無法使用System.Windows.Form.Timer。
方法B必須在調用線程A的同一線程中調用。
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
}
實際上,代碼可以工作,但是我認為這有點令人困惑。 您是否知道有最佳實踐來達到相同的結果?
停留在.NET 2.0上真可惜,因為Rx擴展具有Throttle
方法,可以非常優雅地實現此效果。
遺憾的是,Rx至少需要.NET 3.5 SP1。
那好吧! 您始終可以使用System.Threading.Timer
來完成此操作。 可以通過利用當前的SynchronizationContext
(這是BackgroundWorker
所做的事情)來提供SynchronizationContext
。
這是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 Forms應用程序來演示此類。 該表單包含一個LaggedMethodPair
成員,超時為2000 ms。 它的primaryAction
將項目添加到列表視圖。 它的laggedCallback
將突出顯示的項目添加到列表視圖。
您可以看到代碼按預期運行。
我會將這種功能封裝到帶有其他類可以訂閱的事件(例如timer.tick事件)的計時器類中。
我正在嘗試使用AutoResetEvent
,因為它能夠等待信號。 我用它來讓worker等待來自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()中進行同步。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.