[英]Using lock with Threading.Timer
我有一個Windows服務應用程序,它使用Threading.Timer
和TimerCallback
以特定的間隔進行一些處理。 我需要一次將此處理代碼鎖定為僅1個線程。
因此,例如,啟動服務並觸發第一個回調並啟動線程並開始處理。 只要在下一次回調之前完成處理,這就可以正常工作。 所以說比如說處理比平常花費的時間稍長,並且在另一個線程正在處理時再次觸發TimerCallback,我需要讓該線程等到另一個線程完成。
這是我的代碼示例:
static Timer timer;
static object locker = new object();
public void Start()
{
var callback = new TimerCallback(DoSomething);
timer = new Timer(callback, null, 0, 10000);
}
public void DoSomething()
{
lock(locker)
{
// my processing code
}
}
這是一種安全的方法嗎? 如果隊列變得相當大,會發生什么? 有更好的選擇嗎?
如果您可以使用它們之間的恆定間隔觸發事件(與以恆定間隔觸發它們的當前代碼相反),那么您可以在沒有句點的情況下啟動計時器,並且每次排隊新的回調時,例如
static Timer timer;
public void Start()
{
var callback = new TimerCallback(DoSomething);
timer = new Timer(callback, null, 0, Timeout.Infinite);
}
public void DoSomething()
{
try
{
// my processing code
}
finally
{
timer.Change(10000, Timeout.Infinite);
}
}
此代碼告訴新創建的計時器立即觸發,僅一次。 在處理代碼中,它完成工作,然后告訴計時器在10秒內再次觸發,僅一次。 因為定時器現在不是定期觸發但是通過其回調方法重新啟動,所以回調保證是單線程的,沒有隊列。
如果你想保持一個恆定的間隔,那么它有點棘手,因為你必須決定如果處理開始花費的時間超過定時器間隔該怎么辦。 一種選擇是執行您當前正在執行的操作,但這實際上會導致大量排隊的線程和最終的線程池飢餓。 另一種選擇是,如果已經有一個回調,則簡單地丟棄回調,例如
static Timer timer;
static object locker = new object();
public void Start()
{
var callback = new TimerCallback(DoSomething);
timer = new Timer(callback, null, 0, 10000);
}
public void DoSomething()
{
if (Monitor.TryEnter(locker))
{
try
{
// my processing code
}
finally
{
Monitor.Exit(locker);
}
}
}
如果處理代碼執行時間超過10秒,最糟糕的情況是,每次調用一個新的回調時,你將浪費1個線程池線程(它們將在lock語句中等待)。 如果你采取所有線程池線程HttpWebRequest,ASP.NET,異步委托調用...將受到影響。
我要做的是立即安排第一次回調。 to be called every 10s: 然后,如果你真的需要每隔10秒調用一次 :
public void DoSomething ()
{
DateTime start = DateTime.UtcNow;
...
TimeSpan elapsed = (DateTime.UtcNow - start);
int due_in = (int) (10000 - elapsed.TotalMilliseconds);
if (due_in < 0)
due_in = 0;
timer.Change (due_in, Timeout.Infinite);
}
或者那條線上的東西。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.