Asynchronous Tasks Only Runs a Last Task in List [duplicate] - c#

This question already has an answer here:
Only last task runs!
(1 answer)
Closed 6 years ago.
I am trying to understand how I might be able to make better use of the .Net 4.5 TPL. Traditionally I used to manage the threads the old-school way, queueing and managing the threads directly.
I have created a silly program to explore the TPL, however, the code only seems to execute the last of the tasks added to my tasks list - I cannot determine why:
class Program {
static void Main(string[] args) {
var a = new A();
var b = new B();
var tasks = new List<Task<string>>();
for (int j = 33; j < 64; j++) {
tasks.Add(Task.Run(() => a.Go(j, 20)).ContinueWith((i) => b.Do(i)));
}
var finalTask = Task.Factory.ContinueWhenAll(tasks.ToArray(), r => {
foreach (var t in r)
Console.Write(t.Result);
});
finalTask.Wait();
Console.WriteLine("Finished.");
Console.ReadLine();
}
}
public class A {
public int Go(int chr, int loops) {
for (int i = 0; i < loops; i++) {
Thread.Sleep(10);
Console.Write((char)chr);
}
return loops;
}
}
public class B {
public string Do(Task<int> value) {
string s = "";
for (int i = 0; i < value.Result; i++) {
s = s + "a";
}
return s;
}
}
Any ideas why the other threads do not execute?

You have to capture the running variable j within the closure context:
for (int j = 33; j < 64; j++) {
var num = j;
tasks.Add(Task.Run(() => a.Go(num, 20)).ContinueWith((i) => b.Do(i)));
}
See also Captured variable in a loop in C#

Related

Cannot understand the behaviour of the threads C#

static void Main(string[] args)
{
var sw = new Stopwatch();
sw.Start();
int noOfThreads = Environment.ProcessorCount;
//int minVal = 1;
int maxVal = 10000000;
int blockSize = maxVal / noOfThreads;
List<Thread> threads = new List<Thread>();
List<List<int>> results = new List<List<int>>();
object thisLock = new object();
for (int i = 0; i < noOfThreads; ++i)
{
lock(thisLock)
{
Thread th = new Thread(() =>
{
results.Add(GetPrimeNumbers(i * blockSize, i * blockSize + blockSize));
});
th.Start();
threads.Add(th);
}
}
foreach (var elem in threads)
elem.Join();
}
private static List<int> GetPrimeNumbers(int low, int high)
{
List<int> result = new List<int>();
//Debug.WriteLine("Low: {0}. High: {1}", low, high);
for(int i = low; i <= high; ++i)
{
if (IsPrime(i))
result.Add(i);
}
return result;
}
static bool IsPrime(int number)
{
if (number % 2 == 0)
return false;
else
{
var topLimit = (int)Math.Sqrt(number);
for (int i = 3; i <= topLimit; i += 2)
if (number % i == 0)
return false;
return true;
}
}
With the above code, I was expecting that when I put breakpoint in the GetPrimeNumbers(int low, int high) I would see range of values for low and high, e.g: (0, 1250000), (1250000, 2500000).....(8750000, 10000000). But what I observing is that there are certain blocks that gets passed multiple times - (2500000, 3750000) while certain do not passed at all -(0, 1250000) and this behaviour also matches the results I am getting.
I am curious why I am seeing this behaviour. Is there a way to prevent this?
I am aware of the fact that I can use Parallel.For() and over here I do see the expected behaviour at breakpoint in GetPrimes(int low, int high). But as I mentioned before I am curious why I am seeing the former behaviour.
Thanks in advance!
The problem is that a for loop reuses the same i variable across iterations, and your thread delegate is closing over that variable.
There are various ways to fix this. A simple one is to use a new variable declared within your loop:
for (int i = 0; i < noOfThreads; ++i)
{
int j = i; // capture the value
lock(thisLock)
{
Thread th = new Thread(() =>
{
results.Add(GetPrimeNumbers(j * blockSize, j * blockSize + blockSize));
});
th.Start();
threads.Add(th);
}
}
This still has other issues, though. I'd recommend something more like this:
var allPrimeNumbers = Enumerable.Range(0, numberOfThreads)
.AsParallel()
.SelectMany(i => GetPrimeNumbers(i * blockSize, i * blockSize + blockSize))
.ToList();
Further Reading
Is there a reason for C#'s reuse of the variable in a foreach?
StriplingWarrior had it close, but as mentioned in the comments, you still have a threading bug. You need to move the lock inside the Thread action. Also, to get the best performance, hold the lock for the shortest amount of time possible, which is when modifying the shared results variable. To do that I separated the GetPrimeNumbers call from the results.Add call.
for (int i = 0; i < noOfThreads; ++i)
{
int j = i; // capture the value
Thread th = new Thread(() =>
{
result = GetPrimeNumbers(j * blockSize, j * blockSize + blockSize);
lock(thisLock)
{
results.Add(result);
}
});
th.Start();
threads.Add(th);
}
Also, unless you really need to manage your own threads I would recommend using Tasks (TPL) instead. Here is a modification using Tasks
Task<List<int>> tasks = new Task<List<int>>();
for (int i = 0; i < noOfThreads; ++i)
{
int j = i; // capture the value
tasks.Add(Task.Run(() => GetPrimeNumbers(j * blockSize, j * blockSize + blockSize)));
}
Task.WaitAll(tasks);
results = tasks.Select(t => t.Result).ToList();

Share method between threads

I have a Func like this:
int loopMax = 10, taskMax = 10;
int executionCounter = 0;
Func<int> calculator = new Func<int>(() =>
{
executionCounter++;
int result = 0;
for (int i = 0; i < loopMax; i++)
{
Thread.Sleep(100);
if (result + i >= int.MaxValue)
result = 0;
result += i;
}
return result;
});
Which could be called by multiple threads. For example like this:
Task[] tasks = new Task[taskMax];
for (int i = 0; i < taskMax; i++)
{
tasks[i] = Task.Run(() => _=calculator());
}
Task.WaitAll(tasks);
I need to share the calculator function across all threads, and makes this function being called only once. In fact the executionCounter variable's value after running this code should remain 1, and all of the threads should have the same return value.
UPDATE 1
I think I can solve it if I find a way to server the first thread and block every other threads and after completion of first thread's method call, signal the methods result to other threads and also cancel them, to prevent them calling calculator again.
Using lock inside the method also is not what I am looking for, because in that case again the calculator is being called multiple times...
It seems that you need the Lazy<T> class. This class provides support for lazy initialization. Here is how you could use it:
Lazy<int> lazyCalculator = new Lazy<int>(calculator);
Task[] tasks = new Task[taskMax];
for (int i = 0; i < taskMax; i++)
{
tasks[i] = Task.Run(() => _ = lazyCalculator.Value);
}
Task.WaitAll(tasks);
When a Lazy instance is constructed, it can take an optional LazyThreadSafetyMode argument. The default value of this argument is ExecutionAndPublication, with the behavior described below:
Locks are used to ensure that only a single thread can initialize a Lazy<T> instance in a thread-safe manner.
It looks like you want is that your Calculator method can be executed by any thread, but this method should be executed only once. If it is true, then we would use lock statement.
The purpose of lock statement is:
The lock statement acquires the mutual-exclusion lock for a given
object, executes a statement block, and then releases the lock
An example:
static object lockCalculatorMethod = new object();
static int executionCounter = 0;
static int loopMax = 10;
static int taskMax = 10;
static void Main(string[] args)
{
Task[] tasks = new Task[taskMax];
for (int i = 0; i < taskMax; i++)
{
tasks[i] = Task.Run(() => _ = Calculator());
}
Task.WhenAll(tasks);
}
and Calculator method:
static int Calculator()
{
lock (lockCalculatorMethod)
{
if (executionCounter < 1)
{
executionCounter++;
int result = 0;
for (int i = 0; i < loopMax; i++)
{
Thread.Sleep(100);
if (result + i >= int.MaxValue)
{
result = 0;
result += i;
}
}
return result;
}
else
return -1;
}
}
UPDATE:
If you want to cache result and avoid recalculation while calling by other threads, then you can use threadSafe collection ConcurrentQueue<T> and just get items from this collection:
static object lockCalculatorMethod = new object();
static ConcurrentQueue<int> queue = new ConcurrentQueue<int>();
static int executionCounter = 0;
static int loopMax = 7;
static int taskMax = 7;
static void Main(string[] args)
{
Task[] tasks = new Task[taskMax];
for (int i = 0; i < taskMax; i++)
{
tasks[i] = Task.Run(() =>
{
var result = Calculator();
Console.WriteLine(result);
});
}
Task.WaitAll(tasks);
}
And Calculator method:
static int Calculator()
{
int result = 0;
lock (lockCalculatorMethod)
{
int lockResult = 0;
if (executionCounter < 1)
{
executionCounter++;
for (int i = 0; i < loopMax; i++)
{
Thread.Sleep(100);
lockResult += i;
}
queue.Enqueue(lockResult);
}
}
queue.TryPeek(out result);
return result;
}

C# Tasks that work on different, independent objects, synchronization errors still occur, why? [duplicate]

This question already has answers here:
Captured variable in a loop in C#
(10 answers)
Closed 11 months ago.
My program is running tasks in groups of n tasks at once each time.
Each task writes data to a Queue<string> object of his own, provided by an index to a Queue<string> in a List<Queue<string>> of queues.
the tasks do not share data or Queues, yet I still get synchronization errors.
I know the data structures are not Thread-Safe, I don't understand why they should be, and why I get the errors, since each Task has his own data structure, what could cause the errors?
here is a simple code to demonstrate:
class Program
{
static int j = 0;
List<Queue<string>> queueList = new List<Queue<string>>();
public void StartTasts(int n)
{
for (int i = 0; i < n; i++)
queueList.Add(new Queue<string>());
List<Task> tsk = new List<Task>();
for (int TaskGroup = 0; TaskGroup < 10; TaskGroup++)
{ //10 groups of task
//each group has 'n' tasks working in parallel
for (int i = 0; i < n; i++)
{
//each task gets its own and independent queue from the list
tsk.Add(Task.Factory.StartNew(() =>
{
DoWork(j % n);
}));
j++;
}
//waiting for each task group to finish
foreach (Task t in tsk)
t.Wait();
//after they all finished working with the queues, clear queues
//making them ready for the nest task group
foreach (Queue<string> q in queueList)
q.Clear();
}
}
public void DoWork(int queue)
{
//demonstration of generating strings
//and put them in the correct queue
for (int k = 0; k < 10000; k++)
queueList[queue].Enqueue(k + "");
}
static void Main(string[] args)
{
new Program().StartTasts(10);
}
}
this program generate some errors such as:
System.ArgumentException: 'Destination array was not long enough. Check destIndex and length, and the array's lower bounds.'
System.IndexOutOfRangeException: 'Index was outside the bounds of the array.' (at the Queue)
System.AggregateException: One or more errors occurred. ---> System.ArgumentException: Source array was not long enough. Check srcIndex and length, and the array's lower bounds.
and more errors and would not come up on a Serial case.
I would love to understand why because I cant see how these tasks mess up each other's independent Queues.
The problem is normal variable closure issues. Because all tasks share the same instance of the variable j they will all share the same value, most likely what is happening is your loop starts up 10 tasks super quick, but before any of them can get to j % n the value of j has already become 10.
Make a local copy of k that is declared within the scope of the for loop and it should solve your problem.
public void StartTasts(int n)
{
for (int i = 0; i < n; i++)
queueList.Add(new Queue<string>());
List<Task> tsk = new List<Task>();
for (int TaskGroup = 0; TaskGroup < 10; TaskGroup++)
{ //10 groups of task
//each group has 'n' tasks working in parallel
for (int i = 0; i < n; i++)
{
int k = j; // `int k = i;` would work here too and give you the same results.
tsk.Add(Task.Factory.StartNew(() =>
{
DoWork(k % n);
}));
j++;
}
//waiting for each task group to finish
foreach (Task t in tsk)
t.Wait();
//after they all finished working with the queues, clear queues
//making them ready for the nest task group
foreach (Queue<string> q in queueList)
q.Clear();
}
}
If you want to see the problem in action with a simpler recreation, try this simple code instead.
public static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
int j = i;
Task.TaskFactory.StartNew(() =>
{
Thread.Sleep(10); //Give a little time for the for loop to complete.
Console.WriteLine("i: " + i + " j: " + j);
}
});
Console.ReadLine();
}
You have calculated the taskId inside of the task and changed the base for the calculation outside of the task.
I have changed the logic only slightly. I have not had any errors.
namespace Project1
{
using System.Collections.Generic;
using System.Threading.Tasks;
internal class Program
{
private static int j = 0;
private readonly List<Queue<string>> queueList = new List<Queue<string>>();
public void StartTasts(int n)
{
for (var i = 0; i < n; i++)
{
this.queueList.Add(new Queue<string>());
}
var taskList = new List<Task>();
for (var taskGroup = 0; taskGroup < 10; taskGroup++)
{
// 10 groups of task
// each group has 'n' tasks working in parallel
for (var i = 0; i < n; i++)
{
// each task gets its own and independent queue from the list
var taskId = j % n;
taskList.Add(
Task.Factory.StartNew(
() =>
{
this.DoWork(taskId);
}));
j++;
}
// waiting for each task group to finish
foreach (var t in taskList)
{
t.Wait();
}
// after they all finished working with the queues, clear queues
// making them ready for the nest task group
foreach (var q in this.queueList)
{
q.Clear();
}
}
}
public void DoWork(int queue)
{
// demonstration of generating strings
// and put them in the correct queue
for (var k = 0; k < 10000; k++)
{
this.queueList[queue].Enqueue(k + string.Empty);
}
}
private static void Main(string[] args)
{
new Program().StartTasts(10);
}
}
}
I do not think you problem is in the que, it seems like it in the list itself might be an issue.
As the rule of using parallel or synchronous processes the list is not a thread save DS.
Try using the thread save DS Like ConcurrentBag Class

What is wrong with my async-await code [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
It's giving me build error (i am using vs 2017), but in error list there is no error found
public static async void Main(string[] args)
{
await LongOP1();
}
public static async Task LongOP1()
{
long x = 0;
await Task.Run(() =>
{
for (int i = 0; i <= 10000; i++)
{
for (int j = 0; j <= 10000; j++)
{
x += i + j;
}
}
});
}
You cannot use the async keyword on the Main method yet.
See this for an alternative, and look at other answers in the thread for an explanation :
https://stackoverflow.com/a/24601591/4587181
Relevant code :
static void Main(string[] args)
{
Task.Run(async () =>
{
// Do any async anything you need here without worry
}).GetAwaiter().GetResult();
}
I prefer to do it this way
public static void Main()
{
Task t = LongOP1();
// Do other stuff here...
t.Wait();
}
public static async Task LongOP1()
{
long x = 0;
await Task.Run(() =>
{
for (int i = 0; i <= 10000; i++)
{
for (int j = 0; j <= 10000; j++)
{
x += i + j;
}
}
});
}

What is the equilavent with this simple thread line of code in C# to task?

I got confused in task class in c#. I want to know what is the equivalent line of my thread code of c# to task?
static void Main(string[] args)
{
int i = 3;
for (int x = 1; x <= i; x++)
{
Thread t = new Thread(RunThread);
t.Start();
}
Console.ReadLine();
}
public static void RunThread()
{
Console.WriteLine("Thread..");
}
Try
for (int x = 1; x <= i; x++)
{
Task.Factory.StartNew(RunThread);
}

Categories

Resources