i start 4 threads in a loop. each thread gets a reference to an array element to write the result.
But on the line where i create each thread, i get a System.IndexOutOfRangeException. I'm amazed that the index "i" is going out of range.
here is an example:
void ThreadsStarter()
{
double[] data = new double[4];
for(int i = 0; i < 4; i++)
{
Thread my_thread = new Thread(() => Work(data[i]));
my_thread.Start();
}
}
void Work(double data)
{
}
Why this is happening?
This is a common error: i gets evaluated when threads starts, which happens after the loop has ended. Make a temp, assign i to it, and use temp instead of i in your lambda to fix the issue:
void ThreadsStarter()
{
double[] data = new double[4];
for(int i = 0; i < 4; i++)
{
var temp = i;
Thread my_thread = new Thread(() => Work(ref data[temp]));
my_thread.Start();
}
}
void Work(ref double data)
{
}
Related
I have a very specific and demanding workload I am trying to multithreaded. This is very new to me so I am struggling to find an effective solution.
Program Description: UpdateEquations() is cycling through a list of mathematical functions to update the coordinates of rendered lines. By default, func.Count = 3, so this will call CordCalc() 1500 times every frame. I am using NClac to parse a function string and write the result to the Function list, which will later be used before the end of the frame (irrelevant).
Goal: I want to put each cycle of the for(int a) loop inside its own thread. Since for(int a) will only loop 3 times, I just need to start three threads. I cannot continue the for(int i) loop until for(int a) is fully calculated. I am calculating a very large about of small tasks so it would be too expensive to assign each task to the thread.
What I am currently trying to do: I am trying to use a ThreadPool queue, however I'm not sure how to wait for them all to finish before continuing onto the next for(int i) iteration. Furthermore, while the program compiles and executes, the performance is disastrous. Probably %5 of my original performance. I am not sure if creating a "new WaitCallback" is expensive or not. I was looking for a way to predefined threads somehow so that I don't have to reinitialize them 1500 times a frame. (Which is what I suspect the issue is).
Other things I've tried: I tried using new Thread(() => CordCalc(a, i)); however this seemed to have much worse performance. I saw online somewhere that using a ThreadPool would be less expensive.
(This code is shortened for readability and relevance)
public List<Function> func;
private Expression[] exp;
private int lines_i;
private int lines_a;
public void Start()
{
func = new List<Function>();
exp = new Expression[func.Count];
for (int i = 0; i < func.Count; i++) exp[i] = new Expression(func[i].function);
}
//Calculate
public void CordCalc(object state)
{
for (int b = 0; b < func.Count; b++)
exp[lines_a].Parameters[func[b].name] = func[b].mainCords[lines_i - 1];
exp[lines_a].Parameters["t"] = t;
try
{
func[lines_a].mainCords[lines_i] = Convert.ToSingle(exp[lines_a].Evaluate());
}
catch
{
Debug.Log("input Error");
func[lines_a].mainCords[lines_i] = 0;
}
}
private void UpdateEquations()
{
//Initialize equations
for (int a = 0; a < func.Count; a++)
{
func[a].mainCords[0] = t;
}
lines_i = 1;
for (int i = 1; i < 500; i++)
{
lines_a = 0;
for (int a = 0; a < func.Count; a++)
{
//Calculate
ThreadPool.QueueUserWorkItem(new WaitCallback(CordCalc));
//This was something else that I tried, which gave worse results:
//threads[a] = new Thread(() => CordCalc(a, i));
//threads[a].Start();
//t.Join();
//This was my original method call without multithreading
//func[a].mainCords[i] = CordCalc(a, i);
lines_a++;
}
lines_i++;
}
private void FixedUpdate()
{
t += step * (2 + step) * 0.05f;
UpdateEquations();
}
//Function List
public class Function
{
public string name;
public string function;
public float[] mainCords;
//Constructor
public Function(string nameIn, string funcIn)
{
name = nameIn;
function = funcIn;
}
public void SetSize(int len)
{
mainCords = new float[len];
}
}
I have written the below code in C# to create multiple thread (for ex 10 here)
ThreadStart MainThread = new ThreadStart(CallThread);
for (int i = 1; i <= 10; i++)
{
Thread ChildThread + Convert.ToString(i) = new Thread(MainThread);
ChildThread + Convert.ToString(i).Start();
}
it gives error cannot resolve symble ChildThread in 4th line and Cannot resolve symbol Start in 5th line.
Could someone help me out how to resolve this issue?
You can''t concatenate data to make variable name in c#.
You could simulate the same behavior using a Dictionary:
Dictionary<String, Thread> _threads = new Dictionary<String, Thread>(10);
for (int i = 1; i <= 10; i++)
{
_threads.Add("ChildThread" + Convert.ToString(i),new Thread(MainThread) )
_threads["ChildThread" + Convert.ToString(i)].Start();
}
Edit
On your second requirement you could pass a State to your Thread which could be a list of Files:
List<FileInfo> listOfFiles = //some way to get your files
for (int i = 1; i <= 10; i++)
{
_threads.Add("ChildThread" + Convert.ToString(i),new Thread(MainThread) )
_threads["ChildThread" + Convert.ToString(i)].Start( _listOfFiles.Skip(i*5).Take(5).ToList());
}
And in your CallThread function:
private void CallThread(Object state) {
List<FileInfo> filesToProcess = state as List<FileInfo>;
if(filesToProcess == null) return;
foreach(FileInfo f in filesToProcess) {
//do something
}
}
If you need to have references to your child threads, you can just keep them in list variable:
ThreadStart MainThread = new ThreadStart(CallThread);
List<Thread> threads = new List<Thread>();
for (int i = 1; i <= 10; i++)
{
Thread childThread = new Thread(MainThread);
threads.Add(childThread)
childThread.Start();
}
You cannot create dynamic names for variables. The way that you are approaching is completely wrong for C#. It can be valid for a scripting language (i.e. javascript) though. Here is a small example how you can create them.
ThreadStart MainThread = new ThreadStart(CallThread);
List<Thread> thList = new List<Thread>(); // It will contain all the threads. If you don't need buffering threads, don't use it.
for (int i = 1; i <= 10; i++)
{
Thread th = new Thread(MainThread);
thList.Add(th);
th.Start();
}
I think naming Threads is what you want.
Threads can have a Name property. Example below:
var thread = new Thread(() =>
{
TheMethodYouWantToExecute(passedVariable);
});
thread.Name = "nameThatIsAString";
thread.Start();
The code will create a thread using a lambda expression (doesn't have to be), and will start the thread you've just created.
You can add the "thread" variable to a List<> of Thread objects (Let's name the List<> "threadList"), and later access them by this code below:
var wantedThread = threadList.Where(t => t.Name == "nameThatIsAString").Single();
Have a nice day! If something is unclear, I'll try to explain it better.
I am trying to dynamically create X amount of threads(specified by user) then basically have all of them execute some code at the exact same time in intervals of 1 second.
The issue I am having is that the task I am trying to complete relies on a loop to determine if the current IP is equal to the last. (It scans hosts) So since I have this loop inside, it is going off and then the other threads are not getting created, and not executing the code. I would like them to all go off at the same time, wait 1 second(using a timer or something else that doesnt lock the thread since the code it is executing has a timeout it waits for.) Can anyone help me out? Here is my current code:
int threads = Convert.ToInt32(txtThreads.Text);
List<Thread> workerThreads = new List<Thread>();
string from = txtStart.Text, to = txtEnd.Text;
uint current = from.ToUInt(), last = to.ToUInt();
ulong total = last - current;
for (int i = 0; i < threads; i++)
{
Thread thread = new Thread(() =>
{
for (int t = 0; t < Convert.ToInt32(total); t += i)
{
while (current <= last)
{
current = Convert.ToUInt32(current + t);
var ip = current.ToIPAddress();
doSomething(ip);
}
}
});
workerThreads.Add(thread);
thread.Start();
}
Don't use a lambda as the body of your thread, otherwise the i value isn't doing what you think it's doing. Instead pass the value into a method.
As for starting all of the threads at the same time do something like the following:
private object syncObj = new object();
void ThreadBody(object boxed)
{
Params params = (Params)boxed;
lock (syncObj)
{
Monitor.Wait(syncObj);
}
// do work here
}
struct Params
{
// passed values here
}
void InitializeThreads()
{
int threads = Convert.ToInt32(txtThreads.Text);
List<Thread> workerThreads = new List<Thread>();
string from = txtStart.Text, to = txtEnd.Text;
uint current = from.ToUInt(), last = to.ToUInt();
ulong total = last - current;
for (int i = 0; i < threads; i++)
{
Thread thread = new Thread(new ParameterizedThreadStart(this.ThreadBody, new Params { /* initialize values here */ }));
workerThreads.Add(thread);
thread.Start();
}
lock(syncObj)
{
Monitor.PulseAll(syncObj);
}
}
You're running into closure problems. There's another question that somewhat addresses this, here.
Basically you need to capture the value of i as you create each task. What's happening is by the time the task gets around to actually running, the value of i across all your tasks is the same -- the value at the end of the loop.
I have this code which gives me an "Index was outside the bounds of the array". I don't know why is this happening because variable i should always be less than the length of array bla and therefore not cause this error.
private void buttonDoSomething_Click(object sender, EventArgs e)
{
List<Thread> t = new List<Thread>();
string[] bla = textBoxBla.Lines;
for (int i = 0; i < bla.Length; i++)
{
t.Add(new Thread (() => some_thread_funmction(bla[i])));
t[i].Start();
}
}
Could someone tell me how to fix this and why is this happening. Thanks!
Closures are your problem here.
Basically, instead of grabbing the value when you create the lambda (in the loop), it grabs it when it needs it. And computers are so fast that by the time that happens, it's already out of the loop. And the value's 3.
Here's an example (don't run it yet):
private void buttonDoSomething_Click(object sender, EventArgs e)
{
List<Thread> t = new List<Thread>();
for (int i = 0; i < 3; i++)
{
t.Add(new Thread (() => Console.Write(i)));
t[i].Start();
}
}
Think about what you'd expect the result to be. Would it be 012 you're thinking?
Now run it.
The result will be 333.
Here's some modified code that'll fix it:
private void buttonDoSomething_Click(object sender, EventArgs e)
{
List<Thread> t = new List<Thread>();
string[] bla = textBoxBla.Lines;
for (int i = 0; i < bla.Length; i++)
{
int y = i;
//note the line above, that's where I make the int that the lambda has to grab
t.Add(new Thread (() => some_thread_funmction(bla[y])));
//note that I don't use i there, I use y.
t[i].Start();
}
}
Now it'll work fine. This time the value goes out of scope when the loop finishes, so the lambda has no choice but to take it before the loop finishes. That will get you your expected result, and no exception.
What you are seeing is a race condition. Your for loop is completing prior to the threads actually starting. So by the time the threads actually start the value of i is outside of the bounds of the array.
Try copying the index value and pass in the copy instead.
private void buttonDoSomething_Click(object sender, EventArgs e)
{
List<Thread> t = new List<Thread>();
string[] bla = textBoxBla.Lines;
for (int i = 0; i < bla.Length; i++)
{
int index = i;
t.Add(new Thread (() => some_thread_funmction(bla[index])));
t[i].Start();
}
}
How can I run each call for loop in another thread, but continuation of ExternalMethod should wait to ending of last working thread from for loop (and synchronize) ?
ExternalMethod()
{
//some calculations
for (int i = 0; i < 10; i++)
{
SomeMethod(i);
}
//continuation ExternalMethod
}
One approach would be to use a ManualResetEvent.
Consider the following code (note that this should not be taken as a working example, stuck on OSX so don't have VS nor a C# compiler to hand to check this over):
static ManualResetEvent mre = new ManualResetEvent(false);
static int DoneCount = 0;
static int DoneRequired = 9;
void ExternalMethod() {
mre.Reset();
for (int i = 0; i < 10; i++) {
new Thread(new ThreadStart(ThreadVoid)).Start();
}
mre.WaitOne();
}
void ThreadVoid() {
Interlocked.Increment(ref DoneCount);
if (DoneCount == DoneRequired) {
mre.Set();
}
}
IMPORTANT - This possibly isn't the best way to do it, just an example of using ManualResetEvent, and it will suit your needs perfectly fine.
If you're on .NET 4.0 you can use a Parallel.For loop - explained here.
System.Threading.Tasks.Parallel.For(0, 10, (i) => SomeMethod(i));
One approach is to use a CountdownEvent.
ExternalMethod()
{
//some calculations
var finished = new CountdownEvent(1);
for (int i = 0; i < 10; i++)
{
int capture = i; // This is needed to capture the loop variable correctly.
finished.AddCount();
ThreadPool.QueueUserWorkItem(
(state) =>
{
try
{
SomeMethod(capture);
}
finally
{
finished.Signal();
}
}, null);
}
finished.Signal();
finished.Wait();
//continuation ExternalMethod
}
If CountdownEvent is not available then here is an alternate approach.
ExternalMethod()
{
//some calculations
var finished = new ManualResetEvent(false);
int pending = 1;
for (int i = 0; i < 10; i++)
{
int capture = i; // This is needed to capture the loop variable correctly.
Interlocked.Increment(ref pending);
ThreadPool.QueueUserWorkItem(
(state) =>
{
try
{
SomeMethod(capture);
}
finally
{
if (Interlocked.Decrement(ref pending) == 0) finished.Set();
}
}, null);
}
if (Interlocked.Decrement(ref pending) == 0) finished.Set();
finished.WaitOne();
//continuation ExternalMethod
}
Note that in both examples the for loop itself is treating as a parallel work item (it is on a separate thread from the other work items afterall) to avoid a really subtle race condition that might occur if the first work item signals the event before the next work item is queued.
For .NET 3.5, maybe something like this:
Thread[] threads = new Thread[10];
for (int x = 0; x < 10; x++)
{
threads[x] = new Thread(new ParameterizedThreadStart(ThreadFun));
threads[x].Start(x);
}
foreach (Thread thread in threads) thread.Join();
It may seem counterintuitive to use the Join() method, but since you are effectively doing a WaitAll-type pattern, it doesn't matter what order the joins are executed.