Load testing with Task.Run() inside a loop vs WhenAll() - c#

I wanted to do some simple load test on a piece of code I wrote with multiple threads hitting a piece of code at once.
Now the below code Sample #1 gave me the expected results. But I am not quite sure why Sample #2 didn't give me the same. Could someone explain, please?
Code sample #1
private static FileCreator _fileCreater;
public static void Main(params string[] args)
{
_fileCreater = new FileCreator();
RunTasks().GetAwaiter().GetResult();
}
private static async Task RunTasks()
{
List<Task> tasks = new List<Task>();
for (int i = 0; i < 100000; i++)
{
tasks.Add(Task.Run(() =>
{
_fileCreater.SaveMessage(new Message());
}));
}
await Task.WhenAll(tasks);
}
Initially, I had the below and I expected the same result, but what I noticed was my CPU never went past 20%, which suggested it wasn't really spanning up multiple threads
Code Sample #2
private static FileCreator _fileCreater;
public static void Main(params string[] args)
{
_fileCreater = new FileCreator();
RunTasks().GetAwaiter().GetResult();
}
private static async Task RunTasks()
{
for (int i = 0; i < 100000; i++)
{
await Task.Run(() =>
{
_fileCreater.SaveMessage(new Message());
});
}
}

Your second piece of code is awaiting each task... so no they will not be run concurrently, as you wait for each to finish before starting the next.

Related

Deadlock using async Task and SemaphoreSlim

we are running an ASP.NET 6 webapplication and are having strange issues with deadlocks.
The app suddenly freezes after some weeks of operations and it seems that it might be caused by our locking mechanism with the SemaphoreSlim class.
I tried to reproduce the issue with a simple test-project and found something strange.
The following code is simply starting 1000 tasks where each is doing some work (requesting semaphore-handle, waiting for 10 ms and releasing the semaphore).
I expected this code to simply execute one task after another. But it freezes because of a deadlock in the first call of the DoWork method (at await Task.Delay(10)).
Does anyone know why this causes a deadlock? I tried exactly the same code with ThreadPool.QueueUserWorkItem instead of Task.Run and Thread.Sleep instead of Task.Delay and this worked as expected. But as soon as I use the tasks it stops working.
Here is the complete code-snippet:
internal class Program
{
static int timeoutSec = 60;
static SemaphoreSlim semaphore = new SemaphoreSlim(1);
static int numPerIteration = 1000;
static int iteration = 0;
static int doneCounter = numPerIteration;
static int successCount = 0;
static int failedCount = 0;
static Stopwatch sw = new Stopwatch();
static Random rnd = new Random();
static void Main(string[] args)
{
Task.WaitAll(TestUsingTasks());
}
static async Task TestUsingTasks()
{
while (true)
{
var tasks = new List<Task>();
if (doneCounter >= numPerIteration)
{
doneCounter = 0;
if (iteration >= 1)
{
Log($"+++++ FINISHED TASK ITERATION {iteration} - SUCCESS: {successCount} - FAILURES: {failedCount} - Seconds: {sw.Elapsed.TotalSeconds:F1}", ConsoleColor.Magenta);
}
iteration++;
sw.Restart();
for (int i = 0; i < numPerIteration; i++)
{
// Start indepdent tasks to do some work
Task.Run(async () =>
{
if (await DoWork())
{
successCount++;
}
else
{
failedCount++;
}
doneCounter++;
});
}
}
await Task.Delay(10);
}
}
static async Task<bool> DoWork()
{
if (semaphore.Wait(timeoutSec * 1000)) // Request the semaphore to ensure that one 1 task at a time can enter
{
Log($"Got handle for {iteration} within {sw.Elapsed.TotalSeconds:F1}", ConsoleColor.Green);
var totalSec = sw.Elapsed.TotalSeconds;
await Task.Delay(10); // Wait for 10ms to simulate some work => Deadlock seems to happen here
Log($"RELEASING LOCK handle for {iteration} within {sw.Elapsed.TotalSeconds:F1}. WAIT took " + (sw.Elapsed.TotalSeconds - totalSec) + " seconds", ConsoleColor.Gray);
semaphore.Release();
return true;
}
else
{
Log($"ERROR: TASK handle failed for {iteration} within {sw.Elapsed.TotalSeconds:F1} sec", ConsoleColor.Red);
return false;
}
}
static void Log(string message, ConsoleColor color)
{
Console.ForegroundColor = color;
Console.WriteLine(message);
Console.ForegroundColor = ConsoleColor.White;
}
}
Thanks in advance!
But it freezes because of a deadlock in the first call of the DoWork method (at await Task.Delay(10)).
I would argue that it is not deadlock but a thread starvation issue. If you wait long enough you will see that threads will be able to finish the simulation wait from time to time.
The quick fix here is using non-blocking WaitAsync call with await:
static async Task<bool> DoWork()
{
if (await semaphore.WaitAsync(timeoutSec * 1000))
{
...
}
}
Also note:
It is recommended to wrap the code after Wait.. into try-finally block and release the semaphore in the finally.
Incrementing counters in parallel environments better should be done in atomic fashion, for example with Interlocked.Increment.

For loop async issue C# [duplicate]

This question already has answers here:
Captured variable in a loop in C#
(10 answers)
Closed 2 years ago.
Hi I'm trying to make a simple code to run my function in async way. But the result turn out to be quite unexpected. the result i want is like the counter function can run in parallel way and output the result some way similar like:
Start1
End1
Start2
End2
Start3
Start4
End 3
......
Hi
but it turns out it only get the for loop value i=60 into counter function. I'm quite new to async method and google also cant find the appropriate explanation.
namespace Asycn
{
class Program
{
static async Task Main(string[] args)
{
var tasks = new List<Task>();
for (int i = 0; i < 60; i++)
{
tasks.Add(Task.Run(()=>counters(i)));
}
await Task.WhenAll(tasks);
Console.WriteLine("Hi");
}
private static void counters(int num)
{
Console.WriteLine("Start"+num.ToString());
Thread.Sleep(num*1000);
Console.WriteLine("End"+num.ToString());
}
}
}
And below is the running result
Running Result
I assume that you are just getting familiar with async here. Generally when you want to process this number of tasks, it's better to limit parallelism with something like plinq, or Parallel.Foreach
The issue is that i is incremented before the Tasks run.
All you need to do is capture the value within the loop:
namespace Asycn
{
class Program
{
static async Task Main(string[] args)
{
var tasks = new List<Task>();
for (int i = 0; i < 60; i++)
{
var copy = i; // capture state of i
tasks.Add(Task.Run(()=>counters(copy)));
}
await Task.WhenAll(tasks);
Console.WriteLine("Hi");
}
private static void counters(int num)
{
Console.WriteLine("Start"+num.ToString());
Thread.Sleep(num*1000);
Console.WriteLine("End"+num.ToString());
}
}
}
Your code isn't actually using async/await to its fullest potential. You're not capturing the value of i, but you won't have to if you write your code like this:
static async Task Main(string[] args)
{
var tasks = new List<Task>();
for (int i = 0; i < 60; i++)
{
tasks.Add(counters(i));
}
await Task.WhenAll(tasks);
Console.WriteLine("Hi");
}
private static async Task counters(int num)
{
Console.WriteLine("Start"+num.ToString());
await Task.Delay(num*1000);
Console.WriteLine("End"+num.ToString());
}
The output looks like this:
Start0
End0
Start1
Start2
Start3
...
End1
End2
End3
...
Hi

Printing given array in a reverse order utilizing async-await

I'm learning the usage of async and await, and tried to do the following:
I have an array of numbers in a particular order, and an async method that gets a number and a time delay, and return the same passed-in number.
What I'd like to achieve is to print the numbers in a reveresed order (relative to the calling order), utilizing the time delay.
I'm having a hard time figuring out how to do it, and be glad for a guidance. Here's what I have (which, ofcourse, doesn't work):
public static async Task<int> DelayAndReturn(int number, int milisecDelay)
{
await Task.Delay(milisecDelay);
return number;
}
public static void Main(string[] args)
{
int[] arr = { 1, 2, 3 };
int milisecDelay = 10000;
foreach (int num in arr)
{
Console.WriteLine(DelayAndReturn(num, milisecDelay));
milisecDelay /= 10;
}
Console.ReadLine();
}
DelayAndReturn returns a Task object and the correct way to get the result of that object is to await the task. However, awaiting the task will also stop your foreach until 10000 ms have passed, and only then send the next item for processing.
Note that, although the code execution is waiting for the asynchronous operation to complete, the thread is free to be used by other processes.
Your best bet to get them printed in reversed order, is to create a collection of tasks and await all of them.
public static async Task DelayAndReturn(int number, int milisecDelay)
{
await Task.Delay(milisecDelay);
Console.WriteLine(number);
}
public static void Main(string[] args)
{
PrintReversed().GetAwaiter().GetResult();
}
public static async Task PrintReversed() {
int[] arr = { 1, 2, 3 };
int milisecDelay = 1000;
List<Task> tasks = new List<Task>();
foreach (int num in arr)
{
tasks.Add(DelayAndReturn(num, milisecDelay));
milisecDelay /= 10;
}
await Task.WhenAll(tasks);
}

How to handle threads that hang when using SemaphoreSlim

I have some code that runs thousands of URLs through a third party library. Occasionally the method in the library hangs which takes up a thread. After a while all threads are taken up by processes doing nothing and it grinds to a halt.
I am using a SemaphoreSlim to control adding new threads so I can have an optimal number of tasks running. I need a way to identify tasks that have been running too long and then to kill them but also release a thread from the SemaphoreSlim so a new task can be created.
I am struggling with the approach here so I made some test code that immitates what I am doing. It create tasks that have a 10% chance of hanging so very quickly all threads have hung.
How should I be checking for these and killing them off?
Here is the code:
class Program
{
public static SemaphoreSlim semaphore;
public static List<Task> taskList;
static void Main(string[] args)
{
List<string> urlList = new List<string>();
Console.WriteLine("Generating list");
for (int i = 0; i < 1000; i++)
{
//adding random strings to simulate a large list of URLs to process
urlList.Add(Path.GetRandomFileName());
}
Console.WriteLine("Queueing tasks");
semaphore = new SemaphoreSlim(10, 10);
Task.Run(() => QueueTasks(urlList));
Console.ReadLine();
}
static void QueueTasks(List<string> urlList)
{
taskList = new List<Task>();
foreach (var url in urlList)
{
Console.WriteLine("{0} tasks can enter the semaphore.",
semaphore.CurrentCount);
semaphore.Wait();
taskList.Add(DoTheThing(url));
}
}
static async Task DoTheThing(string url)
{
Random rand = new Random();
// simulate the IO process
await Task.Delay(rand.Next(2000, 10000));
// add a 10% chance that the thread will hang simulating what happens occasionally with http request
int chance = rand.Next(1, 100);
if (chance <= 10)
{
while (true)
{
await Task.Delay(1000000);
}
}
semaphore.Release();
Console.WriteLine(url);
}
}
As people have already pointed out, Aborting threads in general is bad and there is no guaranteed way of doing it in C#. Using a separate process to do the work and then kill it is a slightly better idea than attempting Thread.Abort; but still not the best way to go. Ideally, you want co-operative threads/processes, which use IPC to decide when to bail out themselves. This way the cleanup is done properly.
With all that said, you can use code like below to do what you intend to do. I have written it assuming your task will be done in a thread. With slight changes, you can use the same logic to do your task in a process
The code is by no means bullet-proof and is meant to be illustrative. The concurrent code is not really tested well. Locks are held for longer than needed and some places I am not locking (like the Log function)
class TaskInfo {
public Thread Task;
public DateTime StartTime;
public TaskInfo(ParameterizedThreadStart startInfo, object startArg) {
Task = new Thread(startInfo);
Task.Start(startArg);
StartTime = DateTime.Now;
}
}
class Program {
const int MAX_THREADS = 1;
const int TASK_TIMEOUT = 6; // in seconds
const int CLEANUP_INTERVAL = TASK_TIMEOUT; // in seconds
public static SemaphoreSlim semaphore;
public static List<TaskInfo> TaskList;
public static object TaskListLock = new object();
public static Timer CleanupTimer;
static void Main(string[] args) {
List<string> urlList = new List<string>();
Log("Generating list");
for (int i = 0; i < 2; i++) {
//adding random strings to simulate a large list of URLs to process
urlList.Add(Path.GetRandomFileName());
}
Log("Queueing tasks");
semaphore = new SemaphoreSlim(MAX_THREADS, MAX_THREADS);
Task.Run(() => QueueTasks(urlList));
CleanupTimer = new Timer(CleanupTasks, null, CLEANUP_INTERVAL * 1000, CLEANUP_INTERVAL * 1000);
Console.ReadLine();
}
// TODO: Guard against re-entrancy
static void CleanupTasks(object state) {
Log("CleanupTasks started");
lock (TaskListLock) {
var now = DateTime.Now;
int n = TaskList.Count;
for (int i = n - 1; i >= 0; --i) {
var task = TaskList[i];
Log($"Checking task with ID {task.Task.ManagedThreadId}");
// kill processes running for longer than anticipated
if (task.Task.IsAlive && now.Subtract(task.StartTime).TotalSeconds >= TASK_TIMEOUT) {
Log("Cleaning up hung task");
task.Task.Abort();
}
// remove task if it is not alive
if (!task.Task.IsAlive) {
Log("Removing dead task from list");
TaskList.RemoveAt(i);
continue;
}
}
if (TaskList.Count == 0) {
Log("Disposing cleanup thread");
CleanupTimer.Dispose();
}
}
Log("CleanupTasks done");
}
static void QueueTasks(List<string> urlList) {
TaskList = new List<TaskInfo>();
foreach (var url in urlList) {
Log($"Trying to schedule url = {url}");
semaphore.Wait();
Log("Semaphore acquired");
ParameterizedThreadStart taskRoutine = obj => {
try {
DoTheThing((string)obj);
} finally {
Log("Releasing semaphore");
semaphore.Release();
}
};
var task = new TaskInfo(taskRoutine, url);
lock (TaskListLock)
TaskList.Add(task);
}
Log("All tasks queued");
}
// simulate all processes get hung
static void DoTheThing(string url) {
while (true)
Thread.Sleep(5000);
}
static void Log(string msg) {
Console.WriteLine("{0:HH:mm:ss.fff} Thread {1,2} {2}", DateTime.Now, Thread.CurrentThread.ManagedThreadId.ToString(), msg);
}
}

Update Int32 in Task c#

is there any way to increment a value of an int inside a task?
or is this a correct syntax in incrementing an int in task?
sample code:
public int erCount = 9;
static void Main(string[] args){
Task.Factory.StartNew(() => {
...do some task
if(errorfound)
erCount++;
});
Task.Wait();
Console.Writeline(erCount.toString());
}
I seem not to get it in incrementing value inside a thread.
Any help would be great!
Your code is fine as long as you're not modifying the erCount in multiple threads. In which case you'd need a lock or Interlocked.Increment.
Your problem is you're not waiting for the started Task to complete.
public static int erCount = 9;
static void Main(string[] args)
{
var task = Task.Factory.StartNew(() =>
{
...do some task
if(errorfound)
Interlocked.Increment(ref erCount);
});
task.Wait();//Wait for the task to complete
Console.Writeline(erCount.toString());
}
You may altogether remove the shared field and return the error count. That way you can avoid unnecessary synchronization.
public static int erCount = 9;
static void Main(string[] args)
{
var task = Task.Factory.StartNew(() =>
{
int localErrorCount =0;
...do some task
if(errorfound)
localErrorCount++;
return localErrorCount;
});
int errors = task.Result;//Wait for the task to complete and get the error count
erCount += errors;
Console.Writeline(erCount.toString());
}
You could use Interlocked.Increment():
public int erCount = 9;
static void Main(string[] args){
var task = Task.Factory.StartNew(() =>{
...do some task
if(errorfound)
Interlocked.Increment(ref erCount);
});
task.Wait(); // Wait for the task to complete before showing the error count
Console.Writeline(erCount.toString());
}
The reason its not incrementing is:
Console.Writeline(erCount.toString());
executes before the error count has been incremented.
Move that inside the task at the end, and it should work.
You probably need to have a read up on the Task parrallel library and how multithreading works.

Categories

Resources