using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace ThreadDemo
{
class Program
{
static public List<int> temp = new List<int >();
static public List<Thread> worker = new List<Thread>();
static public List<List<int>> Temporary = new List<List<int>>();
static void Main(string[] args)
{
temp.add(20);
temp.add(10);
temp.add(5);
foreach (int k in temp)
{
int z = 0;
worker[z] = new Thread(() => { sample(k); });
worker[z].Name = "Worker" + z.ToString();
worker[z].Start();
z++;
}
}
public static void sample(int n)
{
List<int> local = new List<int>();
for (int i = 0; i < n; i++)
{
local.Add(i);
}
Temporary.Add(local);
}
}
}
in this program i have the problem in thread when start the foreach loop in main program creates the three thread and also starts that thread.In that first thread operation is longer than other so it will take some time but other thread completed before the first
due to this order in temporary is changed .i need temporary list order as same as temp list order.how can achieve this using thread
There are three problems. First, variable capture:
foreach (int k in temp)
{
int z = 0;
worker[z] = new Thread(() => { sample(k); });
...
}
That's capturing the variable k in the lambda expression, not the value of k. The solution is to take a copy:
foreach (int k in temp)
{
int z = 0;
int copy = k;
worker[z] = new Thread(() => { sample(copy); });
...
}
See Eric Lippert's blog post for more information.
Secondly, you're always populating worker[0] because z will always be 0. If you want to populate other elements, you'll need to declare z outside. Alternatively, you could just add to the list.
Thirdly, there's the problem of not knowing the ordering of the results. The easiest way of fixing this is actually to turn Temporary into an array. Again, capture a copy of the variable to keep the right position. As ArsenMkrt says, you'll also need to update a list, which will involve locking.
Are you using .NET 4.0 (or could you)? Parallel Extensions makes all of this much, much simpler.
Here is a quick stab at your code:
class Program
{
static public List<int> temp = new List<int >();
static public List<Thread> worker = new List<Thread>();
static public List<List<int>> temporary = new List<List<int>>();
static public object sync = new object();
static void Main(string[] args)
{
temp.add(20);
temp.add(10);
temp.add(5);
// Add a corresponding number of lists
for( int i = 0; i < temp.Count; ++i)
{
temporary.Add(new List<int>);
}
// As Jon Skeet mentioned, z must be declared outside the for loop
int z = 0;
foreach (int k in temp)
{
// As Jon Skeet mentioned, you need to capture the value of k
int copy = k;
Thread t = new Thread(() => { Sample(copy, z); });
t.Name = "Worker" + z.ToString();
// set the thread to background, so your thread is
// properly closed when your application closes.
t.IsBackground = true;
t.Start();
// Calling worker[z] will always going to be out of bounds
// because you didn't add anything to to the worker list,
// therefore you just need to add the thread to the worker
// list. Note that you're not doing anything with the worker
// list, so you might as well not have it at all.
worker.Add(t);
z++;
}
}
// Supply the order of your array
public static void Sample(int n, int order)
{
for (int i = 0; i < n; i++)
{
// Technically in this particular case you don't need to
// synchronize, but it doesn't hurt to know how to do it.
lock(sync)
{
temporary[order].Add(i);
}
}
}
Now the temporary list should contain the other lists in the correct order (same as your tmp order). Your title does mention scheduling, but I'm not sure why you need scheduling here or what exactly you're trying to learn about scheduling.
At first, all your threads are access Temporary collection, and because List is not thread safe you should synch your threads to work correctly, and the second, You have no guaranty that the first thread will finish first if the first starts first, it depends how core will schedule threads.
To achieve what you want you can use thread synchronization mechanisms
If you need the threads' results in order, you should probably either pass them your z somehow, or have them return a result and join each of the threads in order in the main thread.
Other than that, you may be kinda screwed. Threads are, by their nature, prone to run at exactly the wrong time no matter what you need to do. You can't rely on them to start or stop exactly when you please; if you could, whole classes of concurrency issues would just go away.
I'd recommend you use threads sparingly. On single-core, single-CPU machines, your threading example would run slower than a decently coded solution without threading, because there's overhead involved in starting a thread. The only time i've ever seen threads be really useful, are times when you'd actually need to be appearing to do two things at once. Like keeping your UI working while you wait for input or do some arbitrarily big job in the background. Having threads for the sake of having them will end up driving you crazy.
Related
This question already has answers here:
Is .NET's StringBuilder thread-safe
(3 answers)
Closed 5 years ago.
using System;
using System.Threading;
using System.Text;
class ThreadTest
{
static StringBuilder sb = new StringBuilder();
static void Main()
{
Thread t = new Thread(WriteY);
t.Start();
for(int i = 0; i < 1000; i++)
{
sb.Append("x");
}
Console.WriteLine(sb.ToString());
}
private static void WriteY()
{
for (int i = 0; i < 1000; i++)
{
sb.Append("y");
}
}
}
output:
{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy}
Question:
Why does 'x' appear before 'y'?
StringBuilder is Accept only one Thread?
Why does not appear like this "xyxyxyxyx xyxyxyxy"?
Questions 1 and 3 are both related to the time slicing of the Windows scheduler. According to the Windows 2000 Performance Guide, a time slice on x86 processors is about 30 ms. That may have changed since Windows 2000, but should still be in this order of magnitude. Hence, t.Start() only adds the new thread to the scheduler but does not immediately trigger a context switch to it. The main thread has still the remaining part of its time slice, which obviously is enough time to print the 'x' 1,000 times.
Furthermore, when the new thread is actually scheduled, it has a whole time slice to print out 'y'. As this is plenty of time, you don't get the pattern "xyxyxy", but rather 'x's until the time slice of the main thread runs out and then 'y's until the end of the time slice of the new thread and then 'x's again. (At least if there are enough 'x's and 'y's to be printed, which, according to Simon's comment is the case with 10,000 'x's and 'y's.)
Question 2 is answered by the MSDN page on the StringBuilder. Under the topic "Thread Safety" it is written that "Any public static ( Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe." As the Append method is an instance method, you cannot call this reliably from different threads in parallel without further synchronization.
Is this what you are looking for?
class Program
{
static StringBuilder sb = new StringBuilder();
static void Main()
{
Thread t = new Thread(WriteY);
t.Start();
for (int i = 0; i < 1000; i++)
{
//Console.Write("x");
sb.Append("x");
Thread.Sleep(10);
}
//t.Join();
Console.WriteLine(sb.ToString());
}
private static void WriteY()
{
for (int i = 0; i < 1000; i++)
{
//Console.Write("y");
sb.Append("y");
Thread.Sleep(10);
}
}
}
Why does 'x' appear before 'y'?
Because the main thread is not blocked at any point and continuing its execution before the resources are granted to other thread that is printing y
StringBuilder is Accept only one Thread?
No that is not the case. Run example below.
Why does not appear like this "xyxyxyxyx xyxyxyxy"?
there is not much work so, to get that random result you need to increase the duration which is demonstrated by using sleep.
Update: in your above example you can see the randomness if you increase your loop to 100000 or greater. and you also need to add t.Join() otherwise you thread may not yield the work.
I am relatively green to proper threading, I have this, it's modified for simplicity but it is essentially the same thing:
//Global:
N=2
bool[] mySwitches;
//In my main:
mySwitches = new bool[N];
for (int i = 0; i < N; i++)
{
ThreadList.Add(new Thread(() => Worker(i)));
ThreadList[i].Start();
}
//Outside of main:
Private Void Worker(int num)
{
while(true)
{
if (mySwitches[num]) //error happes here because num is equal to N, how?
{
//do something
}
}
}
As shown above, somehow a worker thread gets a value of num=N, I expect it to only reach N-1.
I know that i will get incremented after the Worker is created, is i somehow getting passed by reference instead of value?
I tried to fix the issue by putting a test before my while loop to return if num=N, but even with this provision I get the same error. This leads me to believe that num is incremented somehow after the thread starts.
I fixed this issue by putting a Sleep(20) directly after ThreadList[i].Start(), but I don't really like using Sleep, and it's clear I don't know how this threading scenario actually works.
Can anyone shed any light on this?
i is captured by its reference. Change your code as
for (int i = 0; i < N; i++)
{
var thInx = i;
ThreadList.Add(new Thread(() => Worker(thInx)));
ThreadList[thInx].Start();
}
For more info: http://csharpindepth.com/articles/chapter5/closures.aspx
The form I'm trying to develop has an array of 6 picture boxes and an array of 6 die images. I have a button that when clicked needs to create 6 threads that "roll" the dice, showing each image for a moment. The problem I'm having is that I need to call a method within button click after the dice have been rolled. I can get the dice to roll but the message box is displayed immediately. I've tried a few different ways and get various errors. In the non working version below, the program freezes. I've checked out a ton of resources but I'm just not grasping some concepts like Delegates and Invoke all that well.
Any help would be great! Here's my program
namespace testDice
{
public partial class Form1 : Form
{
private Image[] imgAr;
private PictureBox[] picBoxAr;
private Random r;
private Thread[] tArray;
private ThreadStart tStart;
private delegate void setTheImages();
public Form1()
{
InitializeComponent();
setImageArray();
setPicBoxAr();
}
private void setImageArray()
{
imgAr = new Image[6];
imgAr[0] = testDice.Properties.Resources.die6;
imgAr[1] = testDice.Properties.Resources.die1;
imgAr[2] = testDice.Properties.Resources.die2;
imgAr[3] = testDice.Properties.Resources.die3;
imgAr[4] = testDice.Properties.Resources.die4;
imgAr[5] = testDice.Properties.Resources.die5;
}
private void setPicBoxAr()
{
picBoxAr = new PictureBox[6];
picBoxAr[0] = pictureBox1;
picBoxAr[1] = pictureBox2;
picBoxAr[2] = pictureBox3;
picBoxAr[3] = pictureBox4;
picBoxAr[4] = pictureBox5;
picBoxAr[5] = pictureBox6;
}
private void button1_Click(object sender, EventArgs e)
{
roll();
//wait for threads to finish and update images--doesn't work
for (int n = 0; n < 6; n++)
{
while (tArray[n].IsAlive)
{
for (int i = 0; i < 6; i++)
{
this.picBoxAr[i].Update();
}
}
}
MessageBox.Show("Each die has its own thread");
}
private void roll()
{
this.tStart = new ThreadStart(RunAllDiceThreads);
this.tArray = new Thread[6];
for (int i = 0; i < 6; i++)
{
this.tArray[i] = new Thread(tStart);
this.tArray[i].Start();
}
}
private void RunAllDiceThreads()
{
int n = 0;
while (n < 50)
{
setImg();
Thread.Sleep(50);
n++;
}
for (int i = 0; i < 6; i++)
{
if (tArray[i] != null)
{
tArray[i].Abort();
tArray[i] = null;
}
}
}// end RunAllDiceThreads
private void setImg()
{
r = new Random();
for (int i = 0; i < 6; i++)
{
if (this.picBoxAr[i].InvokeRequired)
{
setTheImages s = new setTheImages(setImg);
// parameter mismatch error here
//this.Invoke(s, new object[] { imgAr[r.Next(6)] });
//Freezes here!!
this.Invoke(s);
}
else
{
this.picBoxAr[i].Image = imgAr[r.Next(6)];
}
}
}//end setImg
}// end class Form1
}//end namespace testDice
Sounds like you're getting a deadlock between your invocation of setting the images and your update of the picture boxes.
I'd recommend rethinking your program a bit. Your program almost seems to be built on the concept that you're modeling an individual die with an individual thread. Break up the state of the die from the state of the thread. For example, you might want to create a Die class which has a certain state to it, such as IsRolling, or CurrentValue. Use and modify objects of that class (and that class only) inside your loops in your worker threads. That way, you won't have to invoke back to your UI thread to update. The dependencies are a lot cleaner that way. You might want to create a Timer in your UI thread which periodically fires (say 10-30 times a second), reads the state of each of the dice, and updates the images that way. That's a lot safer in terms of deadlocks because you don't have any cyclic dependencies. It'll also likely produce a more attractive interface because your die images will update in a smoother, more predictable fashion.
Another rule of thumb... Don't call Thread.Abort() (see references). It's generally a lot safer to use a property of a Die object and simply read from that to update your UI.
You need to remove MessageBox.Show("Each die has its own thread"); from button1_Click.
Create a property to track how many threads have returned. When it hits 6 invoke MessageBox.Show("Each die has its own thread"); (you will probably want to put this call in its own method and invoke that method).
Your problem is that you are starting the threads, then while they are running showing the message box rather then waiting for the threads to return.
If you're able to work against the latest version of the .Net Framework, I would recommend making use of the System.Threading.Tasks namespace. The nice thing is that it encapsulates a lot of the multithreading details and makes things much cleaner. Here's a simple example.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TasksExample
{
class Program
{
static void Main(string[] args)
{
// holds all the tasks you're trying to run
List<Task> waitingTasks = new List<Task>();
// a simple object to lock on
object padlock = new object();
// simple shared value that each task can access
int sharedValue = 1;
// add each new task to the list above. The best way to create a task is to use the Task.Factory.StartNew() method.
// you can also use Task.Factory<RETURNVALUE>.StartNew() method to return a value from the task
waitingTasks.Add(Task.Factory.StartNew(() =>
{
// this makes sure that we don't enter a race condition when trying to access the
// shared value
lock (padlock)
{
// note how we don't need to explicitly pass the sharedValue to the task, it's automatically available
Console.WriteLine("I am thread 1 and the shared value is {0}.", sharedValue++);
}
}));
waitingTasks.Add(Task.Factory.StartNew(() =>
{
lock (padlock)
{
Console.WriteLine("I am thread 2 and the shared value is {0}.", sharedValue++);
}
}));
waitingTasks.Add(Task.Factory.StartNew(() =>
{
lock (padlock)
{
Console.WriteLine("I am thread 3 and the shared value is {0}.", sharedValue++);
}
}));
waitingTasks.Add(Task.Factory.StartNew(() =>
{
lock (padlock)
{
Console.WriteLine("I am thread 4 and the shared value is {0}.", sharedValue++);
}
}));
waitingTasks.Add(Task.Factory.StartNew(() =>
{
lock (padlock)
{
Console.WriteLine("I am thread 5 and the shared value is {0}.", sharedValue++);
}
}));
waitingTasks.Add(Task.Factory.StartNew(() =>
{
lock (padlock)
{
Console.WriteLine("I am thread 6 and the shared value is {0}.", sharedValue++);
}
}));
// once you've spun up all the tasks, pass an array of the tasks to Task.WaitAll, and it will
// block until all tasks are complete
Task.WaitAll(waitingTasks.ToArray());
Console.WriteLine("Hit any key to continue...");
Console.ReadKey(true);
}
}
}
I hope this helps, and let me know if you need any more help.
I have a function, like this,
private void Step()
{
foreach (A a in aList)
a.Act();
foreach (B b in bList)
b.Act();
foreach (C c in cList)
c.Act();
}
where "a" gets the first chance "b" next and "c" gets the last chance,
I want a way where every one gets equal chance how do i do it in C#?
Thanks a lot for all the help!!!
I would define an interface IActable (pick your name) which defines Act().
public interface IActable
{
void Act();
}
Make A, B, and C implement IActable. Then, change your function to do the following.
private void Step()
{
var all = new List<IActable>();
all.AddRange(aList);
all.AddRange(bList);
all.AddRange(cList);
all = all.Shuffle(new Random());
foreach (IActable a in all)
{
a.Act();
}
}
Shuffle method, stolen from here
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
T[] elements = source.ToArray();
// Note i > 0 to avoid final pointless iteration
for (int i = elements.Length-1; i > 0; i--)
{
// Swap element "i" with a random earlier element it (or itself)
int swapIndex = rng.Next(i + 1);
T tmp = elements[i];
elements[i] = elements[swapIndex];
elements[swapIndex] = tmp;
}
// Lazily yield (avoiding aliasing issues etc)
foreach (T element in elements)
{
yield return element;
}
}
A parallel implementation using plinq. (order not preserved)
private void Step()
{
var all = new List<IActable>();
all.AddRange(aList);
all.AddRange(bList);
all.AddRange(cList);
all.AsParallel().ForAll(a=>a.Act());
}
ThreadPool.QueueUserWorkItem
Example from the documentation page:
using System;
using System.Threading;
public class Example {
public static void Main() {
// Queue the task.
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc));
Console.WriteLine("Main thread does some work, then sleeps.");
// If you comment out the Sleep, the main thread exits, and
// therefore the application ends, before the work item
// runs on the thread pool thread. The thread pool uses background
// threads, which do not keep the application running. (This
// is a simple example of a race condition.)
Thread.Sleep(1000);
Console.WriteLine("Main thread exits.");
}
// This method performs the work. It will be invoked in a thread
// running in the .NET threadpool.
static void ThreadProc(Object stateInfo) {
// No state object was passed to QueueUserWorkItem, so
// stateInfo is null.
Console.WriteLine("Hello from the thread pool.");
}
}
In your scenario you could simply queue all the workitems from the various lists. There is no guarantee of order-of-processing with the threadpool. However, if the work to be done is fairly "short" - that is to say if the time required for thread dispatch is "about the same as" the time required performing actual work - then you may want to shuffle the actions, so as to provide a fairer chance among all the threadpool threads.
If your actions run for a longish time - let's say tens of seconds or more - then the threadpool will provide good fairness anyway, and assuming no contention among the threads, you probably won't need to shuffle the workitems before queuing them.
You can run all of them in parallel using multiple threads (Cheeso's answer) - note that there are absolutely no guarantees that all methods will run at the same time in this case. You should define what "equal chance" actually means in your case befor jumping into multithreaded approach.
Or you need to decide what order you want you function to be executed and make appropriate ordering. I.e. assign priorities put all of the in the same list (see Brook's answer) and sort them by priority.
I'm running the following code to start my threads, but they don't start as intended. For some reason, some of the threads start with the same objects (and some don't even start). If I try to debug, they start just fine (extra delay added by me clicking F10 to step through the code).
These are the functions in my forms app:
private void startWorkerThreads()
{
int numThreads = config.getAllItems().Count;
int i = 0;
foreach (ConfigurationItem tmpItem in config.getAllItems())
{
i++;
var t = new Thread(() => WorkerThread(tmpItem, i));
t.Start();
//return t;
}
}
private void WorkerThread(ConfigurationItem cfgItem, int mul)
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(10*mul);
}
this.Invoke((ThreadStart)delegate()
{
this.textBox1.Text += "Thread " + cfgItem.name + " Complete!\r\n";
this.textBox1.SelectionStart = textBox1.Text.Length;
this.textBox1.ScrollToCaret();
});
}
Anyone able to help me out?
Starting a thread doesn't really start the thread. Instead it schedules it for execution. I.e. at some point it will get to run when it is scheduled. Scheduling threads is a complex topic and an implementation detail of the OS, so your code should not expect a certain scheduling.
You're also capturing variables in your lambda. Please see this post (there is a section on Captured Variables) for the problems associated with doing that.
You just run into the (be me called) lambda error.
You provide the ConfigurationItem from the foreach loop directly. This leads to the fact, that all your threads get the same item (the last one).
To get this to work you have to create a reference for each item and apply this to each thread:
foreach (ConfigurationItem tmpItem in config.getAllItems())
{
i++;
var currentI = i;
var currentItem = tmpItem;
var t = new Thread(() => WorkerThread(currentItem, currentI));
t.Start();
//return t;
}
And you should also consider using a ThreadPool.
MSDN Description about how to use the ThreadPool
Short summary of differences here on SO
The problem seems to be there : () => WorkerThread(tmpItem, i)
I'm not used to Func<> but it seems to work like anonymous delegates in .NET 2.0. Thus, you may have a reference to the arguments of the WorkerThread() method. Hence, their values are retrieved later (when the thread actually runs).
In this case, you may already be at the next iteration of your main thread...
Try this instead :
var t = new Thread(new ParametrizedThreadStart(WorkerThread));
t.Start(new { ConfigurationItem = tmpItem, Index = i } );
[EDIT] Other implementation. More flexible if you need to pass new parameters to the thread in the future.
private void startWorkerThreads()
{
int numThreads = config.getAllItems().Count;
int i = 0;
foreach (ConfigurationItem tmpItem in config.getAllItems())
{
i++;
var wt = new WorkerThread(tmpItem, i);
wt.Start();
//return t;
}
}
private class WorkerThread
{
private ConfigurationItem _cfgItem;
private int _mul;
private Thread _thread;
public WorkerThread(ConfigurationItem cfgItem, int mul) {
_cfgItem = cfgItem;
_mul = mul;
}
public void Start()
{
_thread = new Thread(Run);
_thread.Start();
}
private void Run()
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(10 * _mul);
}
this.Invoke((ThreadStart)delegate()
{
this.textBox1.Text += "Thread " + _cfgItem.name + " Complete!\r\n";
this.textBox1.SelectionStart = textBox1.Text.Length;
this.textBox1.ScrollToCaret();
});
}
}
Do you really need to spawn threads manually (which is a rather expensive task) ? You could try to switch to the ThreadPool instead.
You can't assume that the threads will run in the same order they were called, unless you force it, and cause a dependency between them.
So the real question is - what is your goal ?
I think that the error is somewhere else. Here are some hints to help you debug :
Give a name containing to each thread, and display the thread name instead of the config item name :
this.textBox1.Text += "Thread " + Thread.Current.Name + " Complete!\r\n";
Display the content of config.getAllItems(), may be that some items has the same name (duplicated)
===========
Here are some additional information about multi threading with winforms:
dont create new Thread directly, use the ThreadPool instead :
ThreadPool.QueueUserWorkItem(state => { WorkerThread(tmpItem, i); });
If you really want to creat your threads, use this.BeginInvoke instead of this.Invoke your worker thread will finish sooner => less concurrent thread => better global performance
don't call Thread.Sleep in a loop, just do a big sleep: Thread.Sleep(10*mul*100);
I hope that this will help you.
Thanks to all of you!
I just implemented the threadpool, and that worked like a charm - with the added bonus of not spawning too many threads at once.
I'll have a look at the other solutions, too, but this time around the threadpool will save me from having to manually check for bozos with too many configs ;)