Skip to content

Commit 3004339

Browse files
committed
Rework threading
1 parent 62b847c commit 3004339

File tree

5 files changed

+95
-52
lines changed

5 files changed

+95
-52
lines changed

Threading/IManageThreads.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
namespace Threading
1+
using System;
2+
3+
namespace Threading
24
{
35
public interface IManageThreads
46
{
57
void Queue(object o);
6-
void ProcessQueue();
8+
void ProcessQueue(Action<object> action, int maxThreads);
79
int NumberThreadsRunning { get; }
810
int MaxConcurrentThreads { get; }
11+
void RemoveThread();
912
}
1013
}

Threading/Program.cs

+44-31
Original file line numberDiff line numberDiff line change
@@ -6,50 +6,63 @@ namespace Threading
66
{
77
class Program
88
{
9+
private static readonly IManageThreads _threadManager = new ThreadManager();
10+
private static int _mainThreadId;
11+
912
static void Main(string[] args)
1013
{
11-
Console.WriteLine($"Main thread Id: {Thread.CurrentThread.ManagedThreadId}");
14+
var rand = new Random();
1215

13-
var mySomethingDoer = new DoSomething
14-
{
15-
Name = "Thing 1",
16-
SleepFactor = 100
17-
};
16+
_mainThreadId = Thread.CurrentThread.ManagedThreadId;
1817

19-
var mySomethingDoer2 = new DoSomething
18+
Console.WriteLine($"Main thread Id: {_mainThreadId}");
19+
20+
//queue up 10 things that need done
21+
for (var i = 1; i < 10; i++)
2022
{
21-
Name = "Thing 2",
22-
SleepFactor = 1000
23-
};
23+
_threadManager.Queue(new DoSomething
24+
{
25+
Id = i,
26+
SleepFactor = rand.Next(1, 5) * 1000
27+
});
28+
}
29+
30+
//work thru the queue, use 2 max threads
31+
_threadManager.ProcessQueue(_workerBee, 5);
2432

25-
var mySomethingDoer3 = new DoSomething
33+
//since work is outside of this thread, we have to wait for the other threads to finish
34+
while (_threadManager.NumberThreadsRunning > 0)
2635
{
27-
Name = "Thing 3",
28-
SleepFactor = 1000
29-
};
36+
37+
}
3038

31-
var mySomethingDoer4 = new DoSomething
32-
{
33-
Name = "Thing 4",
34-
SleepFactor = 100
35-
};
39+
Console.WriteLine("All threads complete.");
40+
Console.WriteLine($"Current number of threads: {_threadManager.NumberThreadsRunning}, Max Threads: {_threadManager.MaxConcurrentThreads}");
41+
Console.ReadKey();
42+
}
3643

37-
var threadManager = new ThreadManager();
44+
//this is the method that does the work
45+
//many threads will be in this method at the same time, so don't assume you get to do whatever you want with out locking
46+
//work inside the DoSomething class should be thread-safe already
47+
private static void _workerBee(object o)
48+
{
49+
//illustrating that the main thread and this method should always be different threads
50+
if (_mainThreadId == Thread.CurrentThread.ManagedThreadId)
51+
{
52+
throw new Exception("This shouldn't be the case!");
53+
}
3854

39-
threadManager.Queue(mySomethingDoer);
40-
threadManager.Queue(mySomethingDoer2);
41-
threadManager.Queue(mySomethingDoer3);
42-
threadManager.Queue(mySomethingDoer4);
55+
//the thread manager is using objects, so we need to cast
56+
var myDoerClass = o as DoSomething;
4357

44-
threadManager.ProcessQueue();
58+
myDoerClass?.Process();
4559

46-
while (threadManager.NumberThreadsRunning > 0)
47-
{
48-
49-
}
60+
//this could be done differently if we choose to want to remove it
61+
//perhaps raise an event that the thread manager subscribes to
62+
//otherwise I don't see an easy way for the thread to be removed from the manager
63+
_threadManager.RemoveThread();
5064

51-
Console.WriteLine("All threads complete.");
52-
Console.ReadKey();
65+
Thread.Sleep(5000);
5366
}
5467
}
5568
}

Threading/ThreadManager.cs

+40-13
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,80 @@
11
using System;
2+
using System.Collections.Concurrent;
23
using System.Collections.Generic;
34
using System.Linq;
45
using System.Threading;
5-
using Threading.Work;
66

77
namespace Threading
88
{
99
public class ThreadManager : IManageThreads
1010
{
11-
private readonly List<int> _currentRunningThreads = new List<int>();
11+
//let's keep track of our threads
12+
private readonly ConcurrentDictionary<int, Thread> _currentRunningThreads = new ConcurrentDictionary<int, Thread>();
13+
14+
//let's keep track of our work to do
1215
private readonly Queue<object> _workQueue = new Queue<object>();
1316

17+
//helper to add a new worker
1418
public void Queue(object o)
1519
{
1620
_workQueue.Enqueue(o);
1721
}
1822

19-
public void ProcessQueue()
23+
//process the queue
24+
//essentially this is the brains that spawns a new thread
25+
//this runs on the main thread
26+
public void ProcessQueue(Action<object> action, int maxThreads)
2027
{
28+
//throttle the work
29+
MaxConcurrentThreads = maxThreads;
30+
31+
//if anything to do...
2132
while (_workQueue.Any())
2233
{
23-
Console.WriteLine($"Work queue items: {_workQueue.Count}");
24-
34+
//using a count on a concurrent dictionary should get us the right count (I hope)
35+
//limit to the max threads
2536
if (_currentRunningThreads.Count < MaxConcurrentThreads)
2637
{
38+
Console.WriteLine($"Work queue items: {_workQueue.Count}");
39+
40+
//grab some work off the top of the queue
2741
var work = _workQueue.Dequeue();
28-
var thread = new Thread(WorkerBee);
2942

30-
_currentRunningThreads.Add(thread.ManagedThreadId);
43+
//define a new thread
44+
var thread = new Thread(new ParameterizedThreadStart(action));
45+
46+
//try to add it to the dictionary
47+
//maybe we should add some logic here in case the dictionary doesn't let us
48+
_currentRunningThreads.TryAdd(thread.ManagedThreadId, thread);
49+
50+
Console.WriteLine($"Spawning new thread: {thread.ManagedThreadId}...");
51+
Console.WriteLine($"Current number of threads: {_currentRunningThreads.Count}, Max Threads: {MaxConcurrentThreads}");
52+
53+
//add some delay so that the console messages slowly crawl
54+
Thread.Sleep(5000);
3155

56+
//begin the work
3257
thread.Start(work);
3358
}
3459
else
3560
{
36-
Console.WriteLine($"Waiting for a thread to come free...");
61+
//guess we should wait until we get a thread to use
62+
//Console.WriteLine($"Waiting for a thread to come free...");
3763
Thread.Sleep(1000);
3864
}
3965
}
4066
}
4167

4268
public int NumberThreadsRunning => _currentRunningThreads.Count;
43-
public int MaxConcurrentThreads => 2;
44-
private void WorkerBee(object o)
69+
public int MaxConcurrentThreads { get; set; }
70+
public void RemoveThread()
4571
{
46-
var myDoerClass = o as DoSomething;
72+
Thread t;
4773

48-
myDoerClass?.Process();
74+
//take the thread out of the mix, this might need better error handling
75+
_currentRunningThreads.TryRemove(Thread.CurrentThread.ManagedThreadId, out t);
4976

50-
_currentRunningThreads.Remove(Thread.CurrentThread.ManagedThreadId);
77+
Console.WriteLine($"Ending thread Id: {t.ManagedThreadId}...");
5178
}
5279
}
5380
}

Threading/Work/DoSomething.cs

+5-5
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@ namespace Threading.Work
55
{
66
public class DoSomething : IDoSomething
77
{
8-
private readonly Random _rand = new Random();
9-
public string Name { get; set; }
8+
//just a placeholder class that is unaware that it's in a multithreaded env
9+
public int Id { get; set; }
1010
public int SleepFactor { get; set; }
1111

1212
public void Process()
1313
{
1414
for (var i = 0; i < 10; i++)
1515
{
16-
Console.WriteLine($"Thread Id: {Thread.CurrentThread.ManagedThreadId} Processing: {Name} - {i + 1}");
16+
Console.WriteLine($"Thread Id: {Thread.CurrentThread.ManagedThreadId} Processing Object: {Id} Task: {i + 1}/10");
1717

18-
Thread.Sleep(_rand.Next(1, 5) * SleepFactor);
18+
Thread.Sleep(SleepFactor);
1919
}
2020

21-
Console.WriteLine($"Thread Id: {Thread.CurrentThread.ManagedThreadId} {Name} is complete.");
21+
Console.WriteLine($"Thread Id: {Thread.CurrentThread.ManagedThreadId} Object {Id} is complete.");
2222
}
2323
}
2424
}

Threading/Work/IDoSomething.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{
33
public interface IDoSomething
44
{
5-
string Name { get; set; }
5+
int Id { get; set; }
66
int SleepFactor { get; set; }
77
void Process();
88
}

0 commit comments

Comments
 (0)