Thread variable issue [duplicate] - c#

This question already has an answer here:
Detailed Explanation of Variable Capture in Closures
(1 answer)
Closed 8 years ago.
I expect the variable in the loop to output 1,2,3 to my listbox below. But it outputs
2
2
2
what is wrong?
C# Code
public partial class Tester : Form
{
public int test = 1;
............................
private void button1_Click(object sender, EventArgs e)
{
test++;
for (int i = 0; i < 3; i++)
{
Task t = Task.Factory.StartNew(() =>
{
System.Threading.Thread.Sleep(5000);
}).ContinueWith(o =>
{
listBox1.Items.Add(test);
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}

If you want to know the sequence in which the Tasks are done you can use the following:
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 3; i++)
{
int tasknumber = test;
Task t = Task.Factory.StartNew(() =>
{
System.Threading.Thread.Sleep(5000);
return tasknumber;
}).ContinueWith(o =>
{
listBox1.Items.Add(o.Result);
}, TaskScheduler.FromCurrentSynchronizationContext());
test++;
}
}
With this code, the value of test is increased at the end of every loop. In the first Task, the tasknumber will be 1. In the second Task the tasknumber will be 2 etc. When the Task is done, the result will be passed to your ListBox.
You might also want to read this blog about StartNew.

Related

Learning task[] [duplicate]

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

Behaviour of new Task with parameters is not the expected one in C# [duplicate]

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
}

Starting Tasks inside a for loop: unexpected behaviour [duplicate]

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.

how to make form delay a display

edit: Application.DoEvents(); this did it. found here: Force GUI update from UI Thread
c#, winforms. i want to increase a number by steps of 1 and have those increments shown inside a listview, so that the user sees the number counting up (for example from 10 to 15).
i have another button that increments just by 1 when clicked and uses the same display(). it works fine and updates the display as expected.
i tried these codes (shortened to save space here):
(1)
for (int i = 0; i < 5; i++)
{
var t = Task.Run (async () =>
{
myInt++;
await Task.Delay(300);
display(); //forces screen refresh
});
}
(2)
for (int i = 0; i < 5; i++)
{
var t = Task.Run (() =>
{
myInt++;
Task.Delay(300).Wait;
display();
});
//t.Wait(); //causes "An unhandled exception of type 'System.AggregateException' occurred in mscorlib.dll"
}
(3)
for (int i = 0; i < 5; i++)
{
myInt++;
display();
System.Threading.Thread.Sleep(300);
}
(4)
Stopwatch stopwatch = new Stopwatch();
for (int i = 0; i < 5; i++)
{
stopwatch.Restart();
while (true)
{
if (stopwatch.ElapsedMilliseconds >= 300)
{
break;
}
}
stopwatch.Stop();
myInt++;
display();
}
all use this:
private void display()
{
myListView.Items.Clear();
myListView.Items.Add(new ListViewItem(new[] { myInt }));
}
(1) and (2) increment the number by 5 but the display is not updated at all. it shows when the display is updated by some other function.
(3) and (4) increment the number by 5, but the display is only updated after about 1500ms, the display skipping the single steps and displaying just the final result (eg 15).
any suggestions to make this work? can i force a refresh in the display() function somehow?
why does t.Wait(); cause an exception? (i found the task code somewhere on the net)
edit:
(5)
private void team_comm_btn_all_Click(object sender, EventArgs e)
{
for (int i = 0; i < 5; i++)
{
await Run(i); //error 74 see below
}
}
private async Task Run(int i)
{
myInt++;
display();
await Task.Delay(300);
}
await Run(i); gives the following:
Error 74 The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
just doing "Run(i)" instead gives a warning that "await" is missing... in this case it compiles and increments by 5 without any delay.
(6)
private void team_comm_btn_all_Click(object sender, EventArgs e)
{
for (int i = 0; i < 5; i++)
{
var task = Task.Run(async () =>
{
await Run(i);
});
}
}
private async Task Run(int i)
{
myInt++;
display();
await Task.Delay(300);
}
increments by 5 but does not update display at all.
Normally you would use System.Windows.Forms.Timer for that. But async/await makes such things trivial as soon as you understand it (and read carefully the compiler warning and error messages). Shortly, (5) is the way to go with a small modification resolving the compiler error.
But let start from the beginning. Assuming you first write a normal synchronous version like this (basically your (3))
private void team_comm_btn_all_Click(object sender, EventArgs e)
{
for (int i = 0; i < 5; i++)
{
if (i != 0) System.Threading.Thread.Sleep(300);
myInt++;
display();
}
}
just to see that the UI is not updating. So you decide to turn it into asynchronous, which with the help of async/await is simple replacing Thread.Sleep with Task.Delay like this
private void team_comm_btn_all_Click(object sender, EventArgs e)
{
for (int i = 0; i < 5; i++)
{
if (i != 0) await Task.Delay(300);
myInt++;
display();
}
}
But now it fails to compile with the following error
Error 74 The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
The message is clear enough. You must mark your method with the async keyword. What about the other recommendation of changing the return type to Task, normally you should take it into consideration, but since you are inside a event handler, i.e. a method which signature cannot be changed, you can ignore that part - that's the exact case why async void is allowed.
To conclude, your final code will look like this
private async void team_comm_btn_all_Click(object sender, EventArgs e)
{
for (int i = 0; i < 5; i++)
{
if (i != 0) await Task.Delay(300);
myInt++;
display();
}
}

System.Threading.Tasks.Parallel [duplicate]

This question already has answers here:
increment a count value outside parallel.foreach scope
(4 answers)
Closed 9 years ago.
I have a trouble with getting perfect result using Parallel.
What I did
protected void Page_Load(object sender, EventArgs e)
{
List<int> listInt = new List<int>();
for (int i = 0; i < 10000; i++)
{
listInt.Add(i);
}
int cnt = 0;
Parallel.ForEach(listInt, num =>
{
cnt++;
}
);
System.Threading.Thread.Sleep(0);
//it should show 10000 but it gives random result
Response.Write(cnt);
}
I was expecting to get 10000 as response but it is giving random result.
What I am doing wrong to get the accurate result.
Live test is here.
Thank you so much.
Your code is not threadsafe.
You can use something like this:
private static readonly object SyncRoot = new object();
and
lock (SyncRoot)
{
cnt++;
}
Check this dotnetfiddle http://dotnetfiddle.net/D7QoP9
Your code is not "threadsafe", that is a "Race".
Add a lock around cnt++ to see the expected result.
Or just use
Interlocked.Increment(ref cnt);

Categories