这是一个设计问题,而不是错误修复问题。
情况就是这样。我有很多集合和对象包含在一个类中。它们的内容仅由单个消息处理程序线程更改。还有一个正在进行渲染的线程。它迭代通过其中一些集合的每个帧,并根据这些对象的值绘制到屏幕。它不会以任何方式改变对象,它只是读取它们的值。
现在渲染完成后,如果更改了任何集合,渲染方法中的foreach循环将失败。我应该如何使这个线程安全?编辑:所以我必须锁定我在其上运行的每个foreach循环之外的集合。这有效,但似乎有很多重复的代码来解决这个问题。
作为一个简短的,做作的例子:
class State
{
public object LockObjects;
public List<object> Objects;
// Called by message handler thread
void HandleMessage()
{
lock (LockObjects)
{
Objects.Add(new object());
}
}
}
class Renderer
{
State m_state;
// Called by rendering thread
void Render()
{
lock (m_state.LockObjects)
{
foreach (var obj in m_state.Objects)
{
DrawObject(obj);
}
}
}
}
这一切都很好,但如果有更好的方法,我宁愿不把锁放在我的所有州集合上。这是“正确”的方式,还是有更好的方法?
答案 0 :(得分:3)
更好的方法是使用开始/结束方法和分离列表为您的线程和同步使用自动事件,例如。它将对您的消息处理程序线程无锁,并使您能够拥有大量的呈现/消息处理程序线程:
class State : IDisposable
{
private List<object> _objects;
private ReaderWriterLockSlim _locker;
private object _cacheLocker;
private List<object> _objectsCache;
private Thread _synchronizeThread;
private AutoResetEvent _synchronizationEvent;
private bool _abortThreadToken;
public State()
{
_objects = new List<object>();
_objectsCache = new List<object>();
_cacheLocker = new object();
_locker = new ReaderWriterLockSlim();
_synchronizationEvent = new AutoResetEvent(false);
_abortThreadToken = false;
_synchronizeThread = new Thread(Synchronize);
_synchronizeThread.Start();
}
private void Synchronize()
{
while (!_abortThreadToken)
{
_synchronizationEvent.WaitOne();
int objectsCacheCount;
lock (_cacheLocker)
{
objectsCacheCount = _objectsCache.Count;
}
if (objectsCacheCount > 0)
{
_locker.EnterWriteLock();
lock (_cacheLocker)
{
_objects.AddRange(_objectsCache);
_objectsCache.Clear();
}
_locker.ExitWriteLock();
}
}
}
public IEnumerator<object> GetEnumerator()
{
_locker.EnterReadLock();
foreach (var o in _objects)
{
yield return o;
}
_locker.ExitReadLock();
}
// Called by message handler thread
public void HandleMessage()
{
lock (_cacheLocker)
{
_objectsCache.Add(new object());
}
_synchronizationEvent.Set();
}
public void Dispose()
{
_abortThreadToken = true;
_synchronizationEvent.Set();
}
}
或者(更简单的方法)你可以使用ReaderWriteerLockSlim
(如果你确定你只有一个阅读器就可以锁定),如下面的代码所示:
class State
{
List<object> m_objects = new List<object>();
ReaderWriterLockSlim locker = new ReaderWriterLockSlim();
public IEnumerator<object> GetEnumerator()
{
locker.EnterReadLock();
foreach (var o in Objects)
{
yield return o;
}
locker.ExitReadLock();
}
private List<object> Objects
{
get { return m_objects; }
set { m_objects = value; }
}
// Called by message handler thread
public void HandleMessage()
{
locker.EnterWriteLock();
Objects.Add(new object());
locker.ExitWriteLock();
}
}
答案 1 :(得分:0)