【发布时间】:2012-07-23 16:46:57
【问题描述】:
我正在尝试实现一个类,该类使用简单的缓存来保存从内部服务检索到的数据。我正在使用 ManualResetEvent 来阻止多个线程,这些线程可能会尝试同时刷新缓存的数据,第一个线程成功地向其他线程发出信号,一旦通过调用 Set() 然后 Reset() 检索到数据就继续。在测试时,我注意到有时所有线程都被释放,有时 1 个或多个线程没有被释放并且被超时,几乎就像我在所有线程被释放之前调用 Reset 一样。有人可以解释我做错了什么吗?
我在下面包含了一个精简版的代码。
private bool _updating;
private const int WaitTimeout = 20000;
private DateTime _lastRefresh;
private object _cacheData;
private readonly ManualResetEvent _signaller = new ManualResetEvent(false);
private void RefreshCachedData()
{
Console.WriteLine("ThreadId {0}: Refreshing Cache", Thread.CurrentThread.ManagedThreadId);
if (_updating)
{
Console.WriteLine("ThreadId {0}: Cache Refresh in progress, waiting on signal.", Thread.CurrentThread.ManagedThreadId);
// another thread is currently updating the cache so wait for a signal to continue
if (!_signaller.WaitOne(WaitTimeout))
Console.WriteLine("ThreadId {0}: Thread timed out ({1}s) waiting for a signal that the cache had been refreshed",
Thread.CurrentThread.ManagedThreadId,WaitTimeout);
Console.WriteLine("ThreadId {0}: Signal recieved to use refreshed cache.", Thread.CurrentThread.ManagedThreadId);
}
else
{
try
{
_updating = true;
var result = _requestHandler.GetNewData();
if (!result.Success)
{
Console.WriteLine("Failed to retrieve new data.");
}
else
{
// switch the cache with the new data
_cacheData = result.Data;
Console.WriteLine(
"ThreadId {0}: Cache refresh success.",
Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(8000);
}
}
catch (Exception ex)
{
Console.WriteLine("Error occured: {0}", ex);
}
finally
{
// Set the refresh date time regardless of whether we succeded or not
_lastRefresh = DateTime.Now;
_updating = false;
// signal any other threads to to carry on and use the refreshed cache
Console.WriteLine("ThreadId {0}: Signalling other threads that cache is refreshed.", Thread.CurrentThread.ManagedThreadId);
_signaller.Set();
_signaller.Reset();
}
}
}
【问题讨论】:
-
Thread.Sleep(8000)和WaitOne听起来不会很好地配合。 -
线程睡眠只是为了测试的目的,以确保所有其他线程在刷新缓存的线程释放它们之前进入等待
-
这可能是一个红鲱鱼,但如果我在设置和重置之间设置一个 50 毫秒的线程睡眠,我还没有在测试中看到任何未释放的线程。
-
我可以在这里重现这种行为(我觉得很奇怪)。您的代码的另一个问题可能是多个线程同时尝试读取和写入 _updating 的竞争条件,可能导致多个线程更新缓存。但这不是这里的原因......
-
这是一个简短的复制品:ideone.com/nuiyu
标签: c# multithreading .net-3.5 manualresetevent