经过我的任务调度程序的一些测试阶段后,我遇到了一个非常随机的死锁。我想问一些帮助,特别是我想知道我的方法是否会陷入僵局,或者问题是否在其他地方。
在开始之前,我会说应用程序是一个全屏游戏。 (如果它可能影响任何事情)
我将用语言解释系统是如何工作的。
任务计划程序
1)每帧开始时每个CPU任务的时间表2。 (通过调度我的意思是设置任务的私有WaitHandle来设置允许任务做一些工作)
这是一个简短的代码,总结了系统对这两项任务的处理方式。
Scheduled++;
InternalLock.Reset();
在安排完所有这两项任务后,首先设置私有WaitHandle。
2)等待所有任务完成。等待内部WaitHandle完成等待,每个任务都必须发出信号。 (每个任务上的WaitOne())
这是等待的代码:
if (Attese.Length > 0)
{
TmpIsSucceded = false;
for (int i = 0; i < Attese.Length; i++)
{
WaitHandle actual = Attese[i].Wait;
do
{
TmpIsSucceded = actual.WaitOne(150);
if (!TmpIsSucceded)
EnginesManager.ProcessMessages();
} while (!TmpIsSucceded);
}
}
任务
1)在比赛结束前永远不会关闭
2)有2个内部WaitHandle。一个私人,告诉他什么时候有工作的任务。当任务结束其工作时发出的一个内部信号。 (任务调度程序正在等待的那个)
3)任务完成后,自动启动任务调度程序的同步(通过lock())队列中的另一个任务(以相同的方式设置该任务的私有等待句柄)。
这是任务的主要循环:
private void CoreThread()
{
while (_active)
{
PrivateLock.WaitOne(-1, false);
while (Scheduled > 0)
{
if (OnThreadExecute != null)
OnThreadExecute(this, null);
Scheduled--;
if (Scheduled == 0)
{
PrivateLock.Reset();
if (OnThreadEnd != null)
OnThreadEnd(this, null);
InternalLock.Set();
}
}
}
}
InternalLock和PrivateLock是两个waitHandles。请注意,InternalLock等待句仅在此代码中设置。没有其他地方设置InternalLock或PrivateLock。 (我发布的代码除外)
当发生死锁时,任务调度程序正在等待所有任务完成,但其中一个任务从未设置InternalLock等待句柄。 “已阻止”任务在“PrivateLock.WaitOne(-1,false);”处停止。发生死锁时的行。
有人对这种僵局有一些线索吗?
编辑:
internal void StartSchedule()
{
for (int i = 0; i < Tasks.Length; i++)
{
if (Tasks[i].Schedule())
QTasks.Enqueue(Tasks[i]);
}
StartThreadAvailable();
}
private void StartThreadAvailable()
{
TempExecList.Clear();
for (int i = 0; i < NThread; i++)
{
if (QTasks.Count > 0)
TempExecList.Add(QTasks.Dequeue());
}
Int32 count = TempExecList.Count;
for (int i = 0; i < count; i++)
TempExecList[i].StartThread();
}
internal void StartThread()
{
PrivateLock.Set();
}
这里是调用Private句柄的Set()的代码。
在这种情况下,Schedule()返回true。 (它只向任务的预定变量添加1并重置InternalLock)
编辑2:
以下是要求的2个类的代码:
http://pastebin.com/m225f839e(GameTask)
http://pastebin.com/m389629cd(TaskScheduler)
答案 0 :(得分:1)
Scheduled
的实现没有显示,但在我看来,这个变量的增量器和减量器之间可能存在竞争。我想你可能需要在这里使用InterlockedDecrement
,如果在Scheduled&gt;测试之间没有空间,我也会更舒服。 0和Scheduled的测试== 0。
更像这样
PrivateLock.WaitOne(-1, false);
while (true)
{
// fetch Sched, so the whole loop sees a single value
int iSched = Scheduled--; // implementation should be Interlocked.Decrement()
if (iSched <= 0)
{
if (iSched < 0)
{
// should never get here, throw exception?
}
PrivateLock.Reset();
if (OnThreadEnd != null)
OnThreadEnd(this, null);
InternalLock.Set();
break; // break out of while
}
if (OnThreadExecute != null)
OnThreadExecute(this, null);
}
答案 1 :(得分:0)
我发现了问题。这是一个非常愚蠢的人......
我会解释所以有这个问题的其他人可以利用我失去的时间。 XD 无论如何,非常感谢John Knoeller通过其洞察力帮助我缩小问题范围。 :d
让我们来看看主线程。 InternalLock.Set()在任务调度程序线程上设置了块,并说“继续”。假设任务调度程序中的任务只有1.想象一下这个可能的情况
1)第一步
任务计划员 - 时间表
任务1 - 等待
2)第二步
任务调度员 - 等待
任务1 - 工作
3)第三步
任务调度员 - 等待
任务1 - InternalLock.Set();
4)第四步
任务计划员 - 时间表
任务1 - | while(计划&gt; 0)|
在第四步导致主线程中的SCHEDULE递增计划变量。这样,while没有结束导致代码中的所有干扰(以及死锁)。 我修复了在InternalLock.Set()之后简单地添加一个中断; 现在没有问题。
(对于那些说我正在访问状态而没有同步的人。请注意,在任何线程被赋予工作之前,调度函数只调用一次,因此无论是否同步都无关紧要。问题是一个非常愚蠢的问题。我的不好。:D)
感谢!!!