代码之家  ›  专栏  ›  技术社区  ›  feal87

僵局,一些问题

  •  1
  • feal87  · 技术社区  · 15 年前

    在对我的任务调度器进行了一些阶段的测试之后,我遇到了一个几乎是随机的死锁。我想寻求一些帮助,特别是我想知道我的方法是否会陷入僵局,或者问题是否在别处。

    我将用文字解释系统是如何工作的。

    1) 在每帧开始时安排2个每CPU任务(通过调度,我的意思是将任务的私有WaitHandle设置为允许任务执行某些工作)

                    Scheduled++;
                    InternalLock.Reset();
    

    在所有这两个任务都安排好之后,通过设置私有WaitHandle启动它们。

    2) 等待所有任务完成。等待是通过等待内部WaitHandle来完成的,该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);
            }
        }
    

    任务


    2) 有2个内部等待句柄。当有工作给他时,告诉任务的一个私人。当任务结束其工作时发出信号的内部程序(任务计划程序正在等待的任务)

    这是任务的主循环:

           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 waithandle。“阻塞”任务在“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();
        }
    

    下面是按要求调用私有句柄的Set()的代码。

    Schedule()在这种情况下始终返回true(它只向任务的计划变量添加1并重置内部锁)

    http://pastebin.com/m225f839e (游戏任务)

    http://pastebin.com/m389629cd (任务调度器)

    2 回复  |  直到 15 年前
        1
  •  1
  •   John Knoeller    15 年前

    实施 Scheduled 没有显示,但在我看来,这个变量的递增和递减之间可能存在竞争。我想你可能需要使用 InterlockedDecrement

    更像这样

    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);
    }
    
        2
  •  0
  •   feal87    15 年前

    我发现了问题。这真是一个愚蠢的决定。。。

    让我们看一下主线。 InternalLock.Set()在任务调度程序线程上设置块并说“继续”。假设任务调度器中的任务只有1。想象一下这种可能的情况

    1) 第一步

    任务调度器-调度


    任务1-工作

    3) 第三步


    4) 第四步

    任务调度器-调度

    在第四步,主线程中的调度增加了调度变量。这样,while并没有结束,而是导致了代码中的所有干扰(以及死锁)。 我通过简单地在InternalLock.Set()之后添加一个中断来修复;

    谢谢