2

I have a program which has multiple threads. User can choose amount of threads to be chosen and that is handled as such:

 for (int i = 0; i <= form.Bots - 1; i++)
        {
            (new Thread(() => {

                doThreadWork();

                form.Console = "Thread(" + Thread.CurrentThread.ManagedThreadId + ") FINISHED " + usedCombos.Count + "/" + form.Combolist.Count;




            })).Start();
        }

doThreadWork is a method each thread has to complete before shutting down. I have an arraylist, which consists of multiple items (lines).

Ex.

value:value
value1:value1
value2:value2
value11:value11
value4:value4
value13:value13

Now the threads exist in my program to make the process of handling these values faster. The program checks which value is valid and which is not. Currently I have implemented a (horrible?) method of choosing the value to be checked for the thread.

int index = GetRandomNumber(0, form.Combolist.Count);

That gets an random index to be chosen in the list. I implemented this because otherwise if I just used a foreach loop, every thread would be checking same value at same time. I need to get something like such:

Imagine following as console logs. Each thread is running at the same time. Values total count is 12. Threads running is 4.

Thread 1: checking index 1
Thread 2: checking index 2
Thread 3: checking index 3
Thread 4: checking index 4
Thread 1: checking index 5
Thread 2: checking index 6
Thread 3: checking index 7
Thread 4: checking index 8
Thread 1: checking index 9
Thread 2: checking index 10
Thread 3: checking index 11
Thread 4: checking index 12
Thread 1: FINISHED
Thread 2: FINISHED
Thread 3: FINISHED
Thread 4: FINISHED

I really hope some of you advanced people could help me out with this, I'm not that advanced :)

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
Coffee Client
  • 37
  • 1
  • 6

3 Answers3

4

C# offers many higher-level ways to perform parallel processing than spawning new threads.

One option you can use is parallel LINQ:

int dop = 4;// set the number of threads to use
form.Combolist
    .AsParallel()
    .WithDegreeOfParallelism(dop)
    .ForAll(i => processElement(i));

You could also use Parallel.ForEach.

Jakub Lortz
  • 14,616
  • 3
  • 25
  • 39
1

I would highly recommend using Task-Parallel-Library (TPL) which has been included in .NET since 4.0. Tasks are not exactly threads, they effectively abstract the thread pool.

This does a great job explaining the subtle differences with-respect to TPL: Multithreading or task parallel library.

    [TestMethod]
    public void ProcessBots()
    {
        string[] bots = new string[] { "A", "B", "C", "D", "E", "F", "G" };
        Parallel.ForEach(bots, bot => //use if you just want to foreach
        {
            this.TestContext.WriteLine(bot);
        });
        Parallel.For(0, bots.Length, i => //use if you care about the index
        {
            this.TestContext.WriteLine(i.ToString());
        });
    }

Test Name:  ProcessBots
Test Outcome:   Passed
Result StandardOutput:  
TestContext Messages:
A
D
E
F
G
B
C
0
2
3
4
5
1
6

Another useful construct if you need to communicate progress is IProgress, it represents a standard contract for pushing results back to your UI on demand.

    [TestMethod]
    public void ProcessBots()
    {
        string[] bots = new string[] { "A", "B", "C", "D", "E", "F", "G" };
        IProgress<string> progress = new Progress<string>(str =>
        {
            this.TestContext.WriteLine(str);
        });
        Parallel.ForEach(bots, bot =>
        {
            progress.Report(bot);
        });
        Parallel.For(0, bots.Length, i =>
        {
            progress.Report(i.ToString());
        });
    }

If your need more streaming like that you might want to look into TPL Dataflow. (nuget package Microsoft.TPL.DataFlow).

    [TestMethod]
    public async Task ProcessBots()
    {
        string[] bots = new string[] { "A", "B", "C", "D", "E", "F", "G" };
        BroadcastBlock<string> getBotsBlock = new BroadcastBlock<string>(bot => bot, new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = ExecutionDataflowBlockOptions.Unbounded });
        Random rand = new Random();
        ActionBlock<string> processBotsBlock = new ActionBlock<string>(async bot =>
        {
            await Task.Delay(rand.Next(100, 1000));
            TestContext.WriteLine(bot);
        }, new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = ExecutionDataflowBlockOptions.Unbounded });
        ActionBlock<string> processBotsToLowerBlock = new ActionBlock<string>(async bot =>
        {
            await Task.Delay(rand.Next(100, 1000));
            TestContext.WriteLine(bot.ToLower());
        }, new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = ExecutionDataflowBlockOptions.Unbounded });
        getBotsBlock.LinkTo(processBotsBlock, new DataflowLinkOptions(){PropagateCompletion = true});
        getBotsBlock.LinkTo(processBotsToLowerBlock, new DataflowLinkOptions() { PropagateCompletion = true });
        foreach (var bot in bots) //note there is no parallelization here, you can post these on demand from any thread completely safe, where as the other solutions if the IEnumerable changes from another thread you have the potential for exceptions
            getBotsBlock.Post(bot);
        getBotsBlock.Complete();
        await processBotsBlock.Completion;
        await processBotsToLowerBlock.Completion;
    }
Test Name:  ProcessBots
Test Outcome:   Passed
Result StandardOutput:  
TestContext Messages:
c
A
G
B
f
b
e
F
C
a
d
g
E
D
Community
  • 1
  • 1
JasonLind
  • 370
  • 2
  • 13
0

In addition to Jakub Lortz's answer, if you want to create your own threads and use a lower level solution:

The simplest solution is to have a shared index variable and only change it under a lock.

//global
private readonly object indexLock = new object();
private int index;
private ArrayList<WorkUnit> workUnits...
//inside doThreadWork
...
lock(indexLock)
{
    if(index > workUnits.Length) return;
    WorkUnit work = WorkUnits[index];
    index += 1;
}
...

A slightly higher level solution would be to use a BlockingCollection. That is in place of an ArrayList have a blocking collection and have each thread just Take a 'work unit' in turn.

All that said Jakub Lortz answer is better and cleaner if it works for you.

pseudoDust
  • 1,336
  • 1
  • 10
  • 18