C#中自制线程池的多线程死锁

Multithreading deadlock on a homemade threading pool in C#

对于我在这里发布这么长的代码,我深表歉意,但我没有其他方法可以帮助您重现我的问题。我给你的代码当然是我正在使用的代码的缩影。 我知道我可以使用 QueueUserWorkItem 直到最近我才使用它,但我意识到我的线程太短以至于我的 ThreadDispatcher 方法在第一个完成之前没有启动第二个。所以我想看看这种方式是否更快。问题是我遇到了僵局,我真的不明白为什么。 我正在发布您可以直接编译和重现问题的代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace TestWhatever
{
    public class Program
    {
        static MyClass ThisClass = new MyClass();
        static void Main()
        {
            ThisClass.Start();
            for (int Cnt = 0; Cnt < 5; Cnt++)
            {
                Console.WriteLine("Launching Frame " + Cnt);
                ThisClass.Update();//Frames loop
            }
        }
    }

    public class MyClass
    {
        Random MyRandom = new Random();

        public static object _MultiDispatcherLocker = new object();
        public static bool MonoDispacherThreadLocked = true;
        public static bool MultiDispacherThreadLocked = true;
        // Thread pool in multithreading case
        private int MaxThreadsInParallel = 5;
        private static int MaxNumOfPool = 20;
        private Thread[] ThreadsPool;
        public static object _ThreadLockerList;
        private Func<string>[] ThreadFunctions;
        public bool[] ThreadLockedBools = Enumerable.Repeat(true, MaxNumOfPool).ToArray();


        public void Start()
        {
            StartThreads();
        }

        public void Update()
        {

            lock (_MultiDispatcherLocker)
            {
                MultiDispacherThreadLocked = false;
                Monitor.Pulse(_MultiDispatcherLocker);
            }
            Thread.Sleep(1000);
        }

        private void StartThreads()
        {
            ThreadsPool = new Thread[MaxNumOfPool];
            _ThreadLockerList = new object();
            ThreadFunctions = new Func<string>[MaxNumOfPool];
            ThreadLockedBools = new bool[MaxNumOfPool];

            for (int Cnt = 0; Cnt < MaxNumOfPool; Cnt++)
            {
                Console.WriteLine("Preparing ThreadID: " + Cnt);
                ThreadLockedBools[Cnt] = true;
                ThreadsPool[Cnt] = new Thread(new ParameterizedThreadStart(LaunchThread));
                ThreadsPool[Cnt].Start(Cnt);
            }

            Thread ThreadedMainThread = new Thread(new ThreadStart(ThreadDispatcher));
            ThreadedMainThread.Start();
            ThreadedMainThread.Priority = System.Threading.ThreadPriority.Highest;
        }

        private void LaunchThread(object iThreadID)
        {
            int ThreadID = (int)iThreadID;
            lock (_ThreadLockerList)
            {
                while (ThreadLockedBools[ThreadID])
                    Monitor.Wait(_ThreadLockerList);
            }
            while (true)
            {
                Console.WriteLine("Starting ThreadID: " + ThreadID);
                ThreadFunctions[ThreadID]();
                Console.WriteLine("Ending ThreadID: " + ThreadID);
                lock (_MultiDispatcherLocker)
                {
                    ThreadLockedBools[ThreadID] = true;
                    MultiDispacherThreadLocked = false;
                    Monitor.Pulse(_MultiDispatcherLocker);
                }
                lock (_ThreadLockerList)
                {
                    Console.WriteLine("Blocking ThreadID: " + ThreadID);
                    while (ThreadLockedBools[ThreadID])
                        Monitor.Wait(_ThreadLockerList);
                }
            }
        }


        private void ThreadDispatcher()//object Obj)
        {
            lock (_MultiDispatcherLocker)
            {
                while (MultiDispacherThreadLocked)
                    Monitor.Wait(_MultiDispatcherLocker);
            }
            while (true)
            {

                for (int Cnt = 0; Cnt < 20; Cnt++)//Threads loop
                {

                    if (RunningThreads() < MaxThreadsInParallel)
                    {
                        int CurrIntTest = MyRandom.Next(100000, 10000000);

                        int ThreadID = GetNextEmptyThread();


                        ThreadFunctions[ThreadID] = () => { MyMethodInThread(CurrIntTest); return null; };
                        lock (_ThreadLockerList)
                        {
                            ThreadLockedBools[ThreadID] = false;
                            Monitor.Pulse(_ThreadLockerList);
                        }
                    }
                    else//wait until someone ends
                    {
                        lock (_MultiDispatcherLocker)
                        {
                            while (MultiDispacherThreadLocked)
                            {
                                Monitor.Wait(_MultiDispatcherLocker);
                            }
                        }
                    }
                }

                lock (_MultiDispatcherLocker)
                {
                    MultiDispacherThreadLocked = true;
                    while (MultiDispacherThreadLocked)
                        Monitor.Wait(_MultiDispatcherLocker);
                }
            }
        }

        private void MyMethodInThread(int Counter)
        {
            List<string> MyDummy = new List<string>();
            for (int Cnt = 0; Cnt < Counter; Cnt++) MyDummy.Add("Dummy");
        }

        private int RunningThreads()
        {
            int ToReturn = 0;
            for (int Cnt = 0; Cnt < MaxThreadsInParallel; Cnt++)
            {
                if (!ThreadLockedBools[Cnt] || ThreadsPool[Cnt].ThreadState != System.Threading.ThreadState.WaitSleepJoin)
                    ToReturn++;
            }
            return ToReturn;
        }

        private int GetNextEmptyThread()
        {
            for (int Cnt = 0; Cnt < MaxThreadsInParallel; Cnt++)
            {
                if (ThreadLockedBools[Cnt] && ThreadsPool[Cnt].ThreadState == System.Threading.ThreadState.WaitSleepJoin)
                    return Cnt;
            }
            return -1;
        }

    }
}

如果你能帮我解决这个问题,那就太棒了。

看看你有什么。

LaunchThread多个线程):

lock (_ThreadLockerList)
{
    while (ThreadLockedBools[ThreadID])
        Monitor.Wait(_ThreadLockerList);
}

ThreadDispatcher线程):

lock (_ThreadLockerList)
{
    ThreadLockedBools[ThreadID] = false;
    Monitor.Pulse(_ThreadLockerList);
}

调用Monitor.Pulse释放的线程不一定是ThreadLockedBools[ThreadID] = false的线程,这种情况下会立即再次进入Monitor.Wait,从而有效吃掉信号。

要解决此问题(通常在这种情况下),请改用 Monitor.PulseAll