Reputation: 179
i apologize for the fact that i am posting here this long code, but i have no other way to help you reproduce my issue. The code i give you of course is a miniature of what i am using.
I know i could be using QueueUserWorkItem
and i was using it until recently, but i realized my threads are so short that that my ThreadDispatcher
method did not launch a second one before the first one finished. So i am trying to see if this way of doing it, is faster or not. The issue is i have a deadlock and i really cant understand why.
I am posting code that you can directly compile and reproduce the issue.
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;
}
}
}
it would be really awesome if you could help me with this.
Upvotes: 2
Views: 63
Reputation: 205649
Let see what you've got.
LaunchThread
(multiple threads):
lock (_ThreadLockerList)
{
while (ThreadLockedBools[ThreadID])
Monitor.Wait(_ThreadLockerList);
}
ThreadDispatcher
(single thread):
lock (_ThreadLockerList)
{
ThreadLockedBools[ThreadID] = false;
Monitor.Pulse(_ThreadLockerList);
}
The thread released by the Monitor.Pulse
call may not be the one with ThreadLockedBools[ThreadID] = false
, in which case it will immediately enter the Monitor.Wait
again, thus effectively eating the signal.
To fix the problem (and in general in such scenarios), use Monitor.PulseAll
instead.
Upvotes: 1