僵局,一些问题

时间:2009-12-26 19:53:24

标签: c# .net multithreading deadlock

经过我的任务调度程序的一些测试阶段后,我遇到了一个非常随机的死锁。我想问一些帮助,特别是我想知道我的方法是否会陷入僵局,或者问题是否在其他地方。
在开始之前,我会说应用程序是一个全屏游戏。 (如果它可能影响任何事情)
我将用语言解释系统是如何工作的。

任务计划程序

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)

2 个答案:

答案 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)

感谢!!!