我为.NET编写了一个多线程应用程序,在代码的一个非常重要的部分我有以下内容:
public class ContainerClass {
private object list_lock;
private ArrayList list;
private object init_lock = new object();
private ThreadClass thread;
public void Start() {
lock(init_lock) {
if (thread == null) {
thread = new ThreadClass();
...
}
}
}
public void Stop() {
lock(init_lock) {
if (thread != null) {
thread.processList(0);
thread.finish();
thread.waitUntilFinished();
thread = null;
} else {
throw new ApplicationException("Assertion failed - already stopped.");
}
...
}
}
private class ThreadedClass {
private ContainerClass container;
private Thread thread;
private bool finished;
private bool actually_finished;
public ThreadedClass(ContainerClass container) {
this.container = container;
thread = new Thread(run);
thread.IsBackground = true;
thread.Start();
}
private void run() {
bool local_finished = false;
while (!local_finished) {
ArrayList to_process = null;
lock (container.list_lock) {
if (container.list.Count > 0) {
to_process = new ArrayList();
to_process.AddRange(container.list);
}
}
if (to_process == null) {
// Nothing to process so wait
lock (this) {
if (!finished) {
try {
Monitor.Wait(this);
} catch (ThreadInterruptedException) {
}
}
}
} else if (to_process.Count > 0) {
// Something to process, so go ahead and process the journals,
int sz = to_process.Count;
// For all elements
for (int i = 0; i < sz; ++i) {
// Pick the lowest element to process
object obj = to_process[i];
try {
// process the element...
...
} catch (IOException e) {
...
// If there is an error processing the best thing to do is finish
lock (this) {
finished = true;
}
}
}
}
lock (this) {
local_finished = finished;
// Remove the elements that we have just processed.
if (to_process != null) {
lock (container.list_lock) {
int sz = to_process.Count;
for (int i = 0; i < sz; ++i) {
container.list.RemoveAt(0);
}
}
}
// Notify any threads waiting
Monitor.PulseAll(this);
}
}
lock (this) {
actually_finished = true;
Monitor.PulseAll(this);
}
}
public void waitUntilFinished() {
lock (this) {
try {
while (!actually_finished) {
Monitor.Wait(this);
}
} catch (ThreadInterruptedException e) {
throw new ApplicationException("Interrupted: " + e.Message);
}
}
}
public void processList(int until_size) {
lock (this) {
Monitor.PulseAll(this);
int sz;
lock (container.list_lock) {
sz = container.list.Count;
}
// Wait until the sz is smaller than 'until_size'
while (sz > until_size) {
try {
Monitor.Wait(this);
} catch (ThreadInterruptedException ) {
}
lock (container.list_lock) {
sz = container.list.Count;
}
}
}
}
}
}
正如您所看到的,线程一直等到集合为空,但似乎同步冲突禁止线程进入该点(整个代码中唯一的一个),其中一个元素从集合中移除{{ 1 {} list
。
如果调用方法ContainerClass
且值processList
为0,则此冲突会导致代码永不返回,应用程序将继续运行。
我求求任何比我更好的开发人员(我想还有很多)帮助我修复这一小段代码,因为我真的不明白为什么列表没有递减...
非常感谢你的心底。
PS。我想强调一下,代码始终完全:从until_size
调用thread.processList(0)
时,它唯一可以用来制动它。
答案 0 :(得分:1)
问题是您是否锁定ThreadClass对象本身而不是同步对象?
尝试添加另一个私有变量来锁定:
private static readonly object lockObject = new object()
并将lock(this)
的所有来电替换为lock(lockObject)
MSDN明确建议不要做什么:
一般情况下,避免锁定公众 类型或代码之外的实例 控制。常见的构造锁定 (this),lock(typeof(MyType))和 锁(“myLock”)违反此规定 准则:
lock (this) is a problem if the instance can be accessed publicly.
修改强>
我想我看到了一个僵局。如果在没有要处理的对象时调用run(),或者没有要处理的对象,则锁定(this),然后调用Monitor.Wait(this)并且线程等待:
if (to_process == null) {
// Nothing to process so wait
lock (this) { /* nothing's going to get this lock again until Monitor.PulseAll(this) is called from somewhere */
if (!finished) {
try {
Monitor.Wait(this); /* thread is waiting for Pulse(this) or PulseAll(this) */
} catch (ThreadInterruptedException) {
}
}
}
}
如果在调用Container.Stop()时处于这种状态,则调用ThreadProcess.processList(int)时,再次调用lock(this),因为run()方法仍然无法进入有锁:
lock (this) { /* run still holds this lock, waiting for PulseAll(this) to be called */
Monitor.PulseAll(this); /* this isn't called so run() never continues */
int sz;
lock (container.list_lock) {
sz = container.list.Count;
}
因此,无法调用Monitor.PulseAll()来释放run()方法中的等待线程以退出锁定(this)区域,因此它们会彼此等待死锁。正确?
答案 1 :(得分:0)
我认为您应该尝试更好地解释您实际想要实现的目标。
public void processList(int until_size) {
lock (this) {
Monitor.PulseAll(this);
这看起来很奇怪,因为你应该在更改锁定状态时调用Monitor.Pulse,而不是在开始锁定时调用。 你在哪里创建工作线程 - 这部分不清楚,因为我只看到Thread.Start()? 顺便说一下,我建议你看看PowerCollections - 也许你找到了你需要的东西。