【发布时间】:2014-10-30 04:11:32
【问题描述】:
我有一个 Windows 服务,它使用带有回调的 System.Threading.Timer 来更新端点,如下所示:
UpdateEndpointTimer = new Timer(
new TimerCallback(UpdateSBEndpoints),
Endpoint.Statuses.Online,
EndpointUpdateFrequency,
EndpointUpdateFrequency);
我的更新方法大致如下:
private void UpdateSBEndpoints(object state)
{
...
using (var context = new TestHarnessContext())
{
var endpoints = context.Endpoints.Where(p =>
p.Binding == Endpoint.Bindings.ServiceBus
&& p.State == Endpoint.States.Enabled
&& p.Status != status).ToList();
foreach (var endpoint in endpoints)
{
//Do stuff here
}
...
}
现在,由于计时器使用 ThreadPool 中的线程来触发回调,我需要采取措施来控制线程。当多个线程可以在第一个线程完成工作之前从数据库中获取相同的端点时,就会出现一个特定的问题,这会导致在 foreach 循环中完成重复的工作。
我知道该问题的两种可能的解决方案,我想知道哪一种更好,更可取。解决方案是ConcurrentDictionary 和ManualResetEvent。
在第一种情况下,我会将它放在我的 foreach 循环中,以确保一次只有一个线程可以在给定端点上运行:
if (EndpointsInAction.TryAdd(endpoint.Id, endpoint.Id) == false)
// If we get here, another thread has started work with this endpoint.
return;
...
//Do stuff with the endpoint, once done, remove its Id from the dictionary
...
int id;
EndpointsInAction.TryRemove(endpoint.Id, out id);
在第二种情况下,我会像这样控制线程:
protected ManualResetEvent PubIsBeingCreated { get; set; }
protected ManualResetEvent SubIsBeingCreated { get; set; }
...
this.PubIsBeingCreated = new ManualResetEvent(true);
this.SubIsBeingCreated = new ManualResetEvent(true);
...
foreach (var endpoint in endpoints)
{
if (!this.PubIsBeingCreated.WaitOne(0))
// If we get here, another thread has started work with this endpoint.
return;
try
{
// block other threads (Timer Events)
PubIsBeingCreated.Reset();
// Do stuff
}
...
finally
{
// Restore access for other threads
PubIsBeingCreated.Set();
}
}
现在这两种方法似乎都有效我想知道哪种方法更可取(更有效?)。我倾向于使用ConcurrentDictionary,因为它允许对线程进行更精细的过滤,即不允许两个线程与特定端点一起工作,而不允许两个线程与特定端点type一起工作(pubs和 ManualResetEvents 中的子项)。可能有另一种解决方案优于我的解决方案,因此任何信息将不胜感激。
【问题讨论】:
-
只需将计时器的 period 参数设置为 0。在方法结束时调用 Change() 以重新启动它。现在它永远不会重叠。
-
如果我正确理解您的问题,计时器会在第一次回调完成之前第二次触发。您可以使用我在此问题的答案中描述的技术来防止这种情况发生:stackoverflow.com/questions/17996147/…
标签: c# multithreading