This question already has answers here:
Starting a new thread in a foreach loop
(5 answers)
Closed 5 years ago.
I am trying to learn C# multithreading. This question might be stupid for some but anyways I will still ask it.
Question:
I'm trying to figure out where should I put (if I'm asking the right question)
the thread.wait() method. So it could show all the child threads in the console. This is the output that I would like to have.
The int num that I inputted is 3.
This should be the output (I am running this with breakpoint):
This is the current output:
Here is my Code:
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Input number of threads: ");
int num = Convert.ToInt32(Console.ReadLine());
Task[] TaskArray = new Task[num];
for (int i = 0; i <= num-1; i++)
{
TaskArray[i] = Task.Run(() => { DifferentMethod(i); });
}
for (int i = 0; i <= num - 1; i++)
{
TaskArray[i].Wait();
}
}
static void DifferentMethod(object ThreadID)
{
while (true)
{
Console.WriteLine("{0} is Running",ThreadID);
Thread.Sleep(500);
}
}
}
}
This has nothing to do with your waiting part.
You need to make a local copy of your variable:
for (int i = 0; i <= num-1; i++)
{
int localCopy = i;
TaskArray[i] = Task.Run(() => { DifferentMethod(localCopy); });
}
Because your i is "captured" (for lack of a better word) but it has already changed by then. So you make a local copy so the capture is on the local copy, that never changes.
You could use the static method from the Task class Task.WaitAll(TaskArray) and the main thread will wait at that point for all tasks to finish
Related
This question already has an answer here:
Issue with multithreading and for loop counter in C#
(1 answer)
Closed 3 years ago.
Main
foreach(Thread t in createTestThreads(8)) {
t.Start();
}
private static Thread[] createTestThreads(int ThreadCount) {
Thread[] threads = new Thread[ThreadCount];
for(int i = 0; i < ThreadCount; i++) {
Thread t = new Thread(() => {
Console.Write(i);
});
threads[i] = t;
}
return threads;
}
Wished Result:
01234567
(Probably not in that exact order, but doesn't have to be)
Actual Result:
88888888
Is there any other way of doing this without naming the Threads manually t0, t1, t2, etc.. Like is there a way to create Threads with dynamical names for example Thread ("t" + i) = new Thread (() =>
i is captured in your lambda; you need to copy it to a temporary variable:
for(int i = 0; i < ThreadCount; i++)
{
int ii = i; // copy value of i to temp variable
Thread t = new Thread(() =>
{
Console.Write(ii); });
threads[ii] = t;
}
}
One solution is to create one child thread to the main thread.
The main thread calls the sub-class createTestThreads and send argument
createTestThreads(8)
The class initialize a thread
Thread threads = new Thread(()=> job(8));
threads.Start();
A method in the class called job
Public void job(int limit)
{
private void job(int ThreadCount)
{
for(int i= 0; i < ThreadCount; i++)
{
Console.WriteLine(i);
}
}
}
This however only initialize one child thread, are you interested in initializing 8 child threads?
This question already has answers here:
List Index Out of Range exception when creating a task
(3 answers)
Closed 3 years ago.
I'm trying to figure out why is this error showing, but I can't. I think that everything's okay, but at some point the value "i" at readTest is == 2. Which is not supposed to... listW has 2 objects, and the "i" should only be 0 and 1. I don't where the 2 is coming from. Am I making anything wrong? I've done some testing and the i=2 only happens at readTest. What's happening?
Thank you for your attention guys
public void readTest(int i)
{
for (int j = 0; j != leftListList[i].getKeyValues().Length; j++)
{
string read = ws.Read(listW[i], wi[i].GetKey(), leftListList[i].getKeyValues()[j]);
WsRead wsRead = wi[i].BuildRead(read, leftListList[i].getKeyValues()[j]);
readList.Add(wsRead);
Console.WriteLine("READ: " + leftListList[i].getKeyValues()[j]);
}
}
public void threadTest()
{
for (int i = 0; i != listW.Length; i++)
{
Thread t = new Thread(() => readTest(i));
t.Start();
}
}
Introduce a local variable, say index:
for (int i = 0; i < listW.Length; i++) // i < listW.Length is more readable
{
int index = i;
...
else
{
// now each thread has its own index
Thread t = new Thread(() => readTest(index));
t.Start();
}
}
when Thread finally starts (it takes time to create a new thread), the loop is completed and thus i == 2
This question already has answers here:
Captured variable in a loop in C#
(10 answers)
Closed 5 years ago.
I am running the following simplified code:
public static void runCode(int num){
Console.WriteLine("Task {0}",num);
for(int j=0;j<10;j++)
Console.Write(num);
}
public static void Main(string[] args){
// some operations here
for(int i=0;i<numIterations;i++){
Console.WriteLine("Current number={0}",i);
Task.Run(()=>runCode(i));
}
// remaining code
}
The result is the following one:
Current number=0
Current number=1
Current number=2
Current number=3
Task 4
4444444444Task 4
4444444444Task 4
4444444444Task 4
4444444444
Why does the shown number is always the same? Maybe for static?
Here is the explanation of this behavior, and that's how you fix it:
public static void runCode(int num)
{
Console.WriteLine("Task {0}", num);
for (int j = 0; j < 10; j++)
Console.Write(num);
}
public static void Main(string[] args)
{
// some operations here
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Current number={0}", i);
var x = i;
Task.Run(() => runCode(x));
}
// remaining code
}
This question already has answers here:
Thread parameters being changed
(2 answers)
Closed 5 years ago.
I am creating a file downloader in .NET which downloads an array of files from a server using Asynchronous tasks. However, even though I create the Task[] and returned string[] with the same length.
Here is my method:
public static string[] DownloadList(string[] urlArray, string[] toPathArray, string login = "", string pass = "", bool getExt = false)
{
Console.WriteLine("DownloadList({0}, {1}, {2}, {3}, {4})", urlArray, toPathArray, login, pass, getExt);
try {
returnedArray = new string[urlArray.Length];
Task[] taskArray = new Task[urlArray.Length];
for (int i = 0; i < urlArray.Length; i++)
{
Thread.Sleep(1000);
Console.WriteLine("i = {0}", i);
Task task = new Task(() => { returnedArray[i] = Download(urlArray[i], toPathArray[i], login, pass, getExt, true); });
task.Start();
taskArray[i] = task;
}
Task.WaitAll(taskArray);
Thread.Sleep(1000);
Console.WriteLine();
Console.WriteLine("Done! Press Enter to close.");
Console.ReadLine();
return returnedArray;
}
catch(Exception e)
{
Console.WriteLine();
Console.WriteLine(e.Message);
Console.ReadLine();
return null;
}
}
and the exception:
which points to this line:
I know it will be something daft that I missed, but I'm rattling my brain trying to figure it out. Thanks for the help in advance!
It looks like Access to Modified Closure problem.
Copy i to local variable:
for (int i = 0; i < urlArray.Length; i++)
{
int index = i;
Thread.Sleep(1000);
Console.WriteLine("i = {0}", index );
Task task = new Task(() => { returnedArray[index] = Download(urlArray[index], toPathArray[index], login, pass, getExt, true); });
task.Start();
taskArray[index] = task;
}
#Backs's answer is correct. Interestingly, in recent versions of .Net, if you use foreach, then the loop variable is closed over. So, you could:
foreach(var i in Enumerable.Range(0, urlArray.Length))
{
... new Task(() => { returnedArray[i] = ...
}
without needing to take a copy.
Looks like you are overshootinhg the array by one, because you count the length of the array in the cycle, and that's 10, while the array goes from 0 to 9.
for (int i = 0; i < urlArray.Length; i++)
Should be
for (int i = 0; i < urlArray.Length - 1; i++)
This question already has answers here:
Captured variable in a loop in C#
(10 answers)
Closed 6 years ago.
I am learning about multithreading in C#. It is making me hard, so I am doing some simple programs to understand it better.
I realized that if I have:
static void Main()
{
Task[] tasks = new Task[4];
for (int i = 0; i < 4; i++)
{
tasks[i] = Task.Factory.StartNew(() => {
Console.WriteLine(i);
});
}
Console.ReadKey();
}
The output is 4444, no 0123 like I expected.
Why is it?
EDIT:
Sadiq said in his answer that the reason of this behaviour is because i'm closing over a loop variable. But if I add to my code Thread.sleep(500); outside the lambda statement and inside the loop, I get 0123.
So, why this behaviour supposedly caused by clousures don't occurs with adding this line of code?
it seems to me that the reason of the asked behaviour is other. The code for if you don't understand what I've just written:
static void Main()
{
Task[] tasks = new Task[4];
for (int i = 0; i < 4; i++)
{
tasks[i] = Task.Factory.StartNew(() => {
Console.WriteLine(i);
});
Thread.Sleep(500);
}
//Now the output is: 0123
Console.ReadKey();
}
Make a local variable - copy the value into it and you should get the expected output:
static void Main()
{
Task[] tasks = new Task[4];
for (int i = 0; i < 4; i++)
{
int x = i;
tasks[x] = Task.Factory.StartNew(() => {
Console.WriteLine(x);
});
}
Console.ReadKey();
}
The reason you get the unexpected output is because i gets shared. You are closing over a loop variable.