ThreadAbortException with await - c#

I'm facing a weird bug.
I have something like 100 long running tasks and I want to run 10 of them in the same time.
I found something very similar to my need here : http://msdn.microsoft.com/en-us/library/hh873173%28v=vs.110%29.aspx in the Throttling section.
Here the C# code after simplification :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
public class Program
{
static void Main(string[] args)
{
Test();
}
public static async void Test()
{
var range = Enumerable.Range(1, 100).ToList();
const int CONCURRENCY_LEVEL = 10;
int nextIndex = 0;
var matrixTasks = new List<Task>();
while (nextIndex < CONCURRENCY_LEVEL && nextIndex < range.Count())
{
int index = nextIndex;
matrixTasks.Add(Task.Factory.StartNew(() => ComputePieceOfMatrix()));
nextIndex++;
}
while (matrixTasks.Count > 0)
{
try
{
var imageTask = await Task.WhenAny(matrixTasks);
matrixTasks.Remove(imageTask);
}
catch (Exception e)
{
Console.Write(1);
throw;
}
if (nextIndex < range.Count())
{
int index = nextIndex;
matrixTasks.Add(Task.Factory.StartNew(() => ComputePieceOfMatrix()));
nextIndex++;
}
}
await Task.WhenAll(matrixTasks);
}
private static void ComputePieceOfMatrix()
{
try
{
for (int j = 0; j < 10000000000; j++) ;
}
catch (Exception e)
{
Console.Write(2);
throw;
}
}
}
}
When running it from a Unit Test a have a ThreadAbortException in ComputePieceOfMatrix.
Do you have any idea ?
Edit :
According to a comment, I tried this :
static void Main(string[] args)
{
Run();
}
private static async void Run()
{
await Test();
}
public static async Task Test()
{
var range = Enumerable.Range(1, 100).ToList();
But it's exactly the same.

1.Your code causes exception
try
{
for (int j = 0; j < 10000000000; j++) ;
}
catch (Exception e)
{
Console.Write(2);
throw;
}
Just a simple OverflowException becase 10000000000 - is long and j counter int.
2.Your main tread is exiting before child threads run to finish. Most likely you are getting ThreadAbortException because Threads are closed by runtime
3.await Test() - correctly just call Test(), and await Task.WhenAny without await as well

Change the return type of Test() to Task, then wait for that Task to finish before your program reaches the end.
static void Main(string[] args)
{
Test().Wait();
}
public static async Task Test()
{
// ...
}

I would change your Test from a void to a Task return type and in the main method I would do in place of Test();
Task t = Test();
t.Wait();

Related

Cancel task that invoke 3rd party long running method

I read some articles how to corectly cancel a task. Most of them describe: "you have to just add ThrowIfCancellationRequested() inside the method that is long running" - and that's it.
Ok - everything is clear but how to do it properly if we cannot modify method?
Please take a look on "DLL" class. I thought about to get current thread and abort it but that operation
is doesn't recommended by most of programmers. So my question is: how to do it properly?
Little example:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace LongRunningTask
{
static class ClassWithAccess
{
public static void DoHeavyOperation(CancellationToken ct)
{
for (int i = 0; i < 10000000; i++)
{
Thread.Sleep(500);
ct.ThrowIfCancellationRequested();
Console.WriteLine("inside local method that we can modify");
}
}
}
static class DLL
{
public static void DoHeavyOperation()
{
for (int i = 0; i < 10000000; i++)
{
Thread.Sleep(500);
//ct.ThrowIfCancellationRequested(); we can't do this
Console.WriteLine("inside DLL");
}
}
}
class Program
{
static void Main(string[] args)
{
CancellationTokenSource src = new CancellationTokenSource();
CancellationToken ct = src.Token;
Console.WriteLine("start");
Task.Run(() =>
{
//ClassWithAccess.DoHeavyOperation(ct);
DLL.DoHeavyOperation();
}, ct);
Thread.Sleep(3000);
src.Cancel();
Console.WriteLine("Cancel");
Console.ReadKey();
}
}
}

Run tasks in parallel and some of them consecutively

I want to run method A and method B1 in parallel. This is working. But how can I run a method B2 after B1 has finished?
class Program
{
static void Main(string[] args)
{
//var firstTask = Task.Factory.StartNew(() => MethodB1());
//var secondTask = firstTask.ContinueWith( (antecedent) => MethodB2());
Action[] actionsArray =
{
() => MethodA(),
() => MethodB1(),
};
Parallel.Invoke(actionsArray);
}
private static void MethodA()
{
Console.WriteLine("A");
// more code is running here (30 min)
}
private static void MethodB1()
{
Console.WriteLine("B1");
// more code is running here (2 min)
}
private static void MethodB2()
{
Console.WriteLine("B2");
}
}
Edit:
I hope the following example will stop the confusion. ;)
A -> A -> A -> A -> A -> A -> A -> A -> A -> A -> A -> A -> A -> A
B1 -> B1 -> B1 -> B1 -> B1 -> B2 -> B2 -> B2
C# is a great language to do this and there are many ways to do it one is as the comment suggests another is this:
static void Main()
{
var t = MethodA();
MethodB1().ContinueWith((r) =>
MethodB2()).Wait();
t.Wait();
}
private static async Task MethodA()
{
await Task.Run(() =>
{
for (int i = 0; i < 40; i++)
{
Thread.Sleep(100);
Console.WriteLine("A");
}
});
}
private static async Task MethodB1()
{
await Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
Thread.Sleep(100);
Console.WriteLine("B1");
}
});
}
private static void MethodB2()
{
for (int i = 0; i < 10; i++)
{
Thread.Sleep(100);
Console.WriteLine("B2");
}
}
You can easily achieve this by using ContinueWith. Here is the code
public static void Main(string[] args)
{
var t1 = Task.Factory.StartNew(MethodA);
var t2 = Task.Factory.StartNew(MethodB1).ContinueWith(task => MethodB2());
Task.WaitAll(t1, t2); // If you want to wait here for finishing the above tasks
}
To depict the output you can try with the following implementation of MethodA, MethodB1 and MethodB2
private static void MethodA()
{
for (int i = 0; i < 100; i++)
{
Console.Write("A ");
Thread.Sleep(100);
}
}
private static void MethodB1()
{
for (int i = 0; i < 100; i++)
{
Console.Write("B1 ");
Thread.Sleep(100);
}
}
private static void MethodB2()
{
for (int i = 0; i < 100; i++)
{
Console.Write("B2 ");
Thread.Sleep(100);
}
}
This will not work with VS2017 and newer because async keyword is not supported on console app mains before VS2017. On older version, instead of await Task.WhenAll() you should do Task.WhenAll().Wait();
namespace ConsoleApp3
{
class Program
{
static async void Main(string[] args)
{
var taskA = Task.Run(() => MethodA()); //Start runnning taskA
var taskB1AndB2 = Task.Run(() => MethodB1()).ContinueWith(async (taskb1) => { await taskb1; await Task.Run(()=>MethodB2()); }).Unwrap(); //When taskB1 is complete, continue with taskB2
await Task.WhenAll(taskA, taskB1AndB2); //wait until all 3 tasks are completed
}
private static void MethodA()
{
Console.WriteLine("A");
// more code is running here (30 min)
}
private static void MethodB1()
{
Console.WriteLine("B1");
// more code is running here (2 min)
}
private static void MethodB2()
{
Console.WriteLine("B2");
}
}
}
One way is Delegate CallBack Mechanism class Program. The another can be event based Here is Used First approach . Given with time samples
This is exactly what you want
class Program
{
static System.Collections.Concurrent.ConcurrentQueue<Action> leftOvers = new System.Collections.Concurrent.ConcurrentQueue<Action>();
static void Main(string[] args)
{
for (int i = 0; i < 100; i++)
{
Task.WaitAll(MethodA(), MethodB1(MethodB2));
}
Action callBack = null;
while (leftOvers.TryDequeue(out callBack))
{
callBack();
}
Console.ReadLine();
}
private static void QueueMethods(Action method)
{
leftOvers.Enqueue(method);
}
private async static Task MethodA()
{
await Task.Run(() => Console.WriteLine("A at" + DateTime.Now.TimeOfDay ));
}
private async static Task MethodB1(Action callBack)
{
await Task.Run(() => Console.WriteLine("B1 at" + DateTime.Now.TimeOfDay));
leftOvers.Enqueue(callBack);
}
private static void MethodB2()
{
Console.WriteLine("B2 at" + DateTime.Now.TimeOfDay);
}
}
Just add B2 after Parallel.Invoke. Parallel.Invoke waits to finish before going to the next line. https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/how-to-use-parallel-invoke-to-execute-parallel-operations
public static void Main(string[] args)
{
Action[] actionsArray =
{
() => MethodA(),
() => MethodB1()
};
Parallel.Invoke(actionsArray);
MethodB2();
}
private static void MethodA()
{
Console.WriteLine("A");
Thread.Sleep(3000);
// more code is running here (30 min)
}
private static void MethodB1()
{
Console.WriteLine("B1");
Thread.Sleep(200);
// more code is running here (2 min)
}
private static void MethodB2()
{
Console.WriteLine("B2");
}

c# multiple threads waiting for a ManualResetEvent

I'm messing around with multithreading and making some sort of task engine. The idea is that the engine can have a configurable amount of threads waiting and when a new task arrives the first free thread picks it up and executes it.
The problem is that something 2 threads pickup the same task somehow. I looked it through and I think that this code should work but obviously it doesn't. If I add the 10ms sleep where it is now commented out it works, but I'm not sure I understand why. It looks like the .Reset() function returns before it actually resets the event?
Can somebody explain? Is there a better way to let only a single thread continue when there are multiple waiting?
Thanks
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace TaskTest
{
public class Engine
{
private ManualResetEvent taskEvent;
private ConcurrentQueue<Task> tasks;
private bool running;
private List<Thread> threads;
private int threadAmount;
private int threadsBusy = 0;
public Engine(int amountOfThreads)
{
taskEvent = new ManualResetEvent(false);
tasks = new ConcurrentQueue<Task>();
threads = new List<Thread>();
threadAmount = amountOfThreads;
}
public void Start()
{
running = true;
for (var i = 0; i < threadAmount; i++)
{
var thread = new Thread(Process);
thread.Name = "Thread " + i;
threads.Add(thread);
thread.Start();
}
}
public void Stop()
{
running = false;
taskEvent.Set();
threads.ForEach(t => t.Join());
}
private void Process()
{
while (running)
{
lock (taskEvent)
{
// Lock it so only a single thread is waiting on the event at the same time
taskEvent.WaitOne();
taskEvent.Reset();
//Thread.Sleep(10);
}
if (!running)
{
taskEvent.Set();
return;
}
threadsBusy += 1;
if (threadsBusy > 1)
Console.WriteLine("Failed");
Task task;
if (tasks.TryDequeue(out task))
task.Execute();
threadsBusy -= 1;
}
}
public void Enqueue(Task t)
{
tasks.Enqueue(t);
taskEvent.Set();
}
}
}
EDIT
Rest of the code:
namespace TaskTest
{
public class Start
{
public static void Main(params string[] args)
{
var engine = new Engine(4);
engine.Start();
while (true)
{
Console.Read();
engine.Enqueue(new Task());
}
}
}
}
namespace TaskTest
{
public class Task
{
public void Execute()
{
Console.WriteLine(Thread.CurrentThread.Name);
}
}
}
When using Console.Read() on a key press, two characters are read from the input. You should use Console.ReadLine() instead.
Note that your code can be simplified a lot by using a BlockingCollection to handle the synchronization:
public class Engine
{
private BlockingCollection<Task> tasks;
private List<Thread> threads;
private int threadAmount;
public Engine(int amountOfThreads)
{
tasks = new BlockingCollection<Task>();
threads = new List<Thread>();
threadAmount = amountOfThreads;
}
public void Start()
{
for (var i = 0; i < threadAmount; i++)
{
var thread = new Thread(Process);
thread.Name = "Thread " + i;
threads.Add(thread);
thread.Start();
}
}
public void Stop()
{
tasks.CompleteAdding();
threads.ForEach(t => t.Join());
}
private void Process()
{
foreach (var task in tasks.GetConsumingEnumerable())
{
task.Execute();
}
}
public void Enqueue(Task t)
{
tasks.Add(t);
}
}

Why my task work only once?

I want to print every 2 sec number but and in the end i get only 0 what i need to do to get this every 2 sec?
result:
0
1
.
.
49
private static void Main(string[] args) {
Send();
}
public static async Task Send() {
for (int i = 0; i < 50; i++) {
Console.WriteLine(i);
await Task.Delay(2000);
}
}
well, simply because your Main method won't wait for the send method to finish and you can't use the await keyword in order for that to happened since the compiler won't allow an async Main, in that case you could simple use a Task.Run
private static void Main(string[] args)
{
Task.Run(async () =>
{
await Send();
}).Wait();
}
public static async Task Send()
{
for (int i = 0; i < 50; i++)
{
Console.WriteLine(i);
await Task.Delay(2000);
}
}
What you are missing is a Console.ReadLine() call, to halt the console from exiting the program before your task gets executed. Tested the code below and it works.
private static void Main(string[] args)
{
Send();
Console.ReadLine();
}
public static async Task Send()
{
for (int i = 0; i < 50; i++)
{
Console.WriteLine(i);
await Task.Delay(2000);
}
}
Building on Josephs answer, but in a more general "How to do asynchronous console apps"-way
Using asynchronous code in console apps can be a bit tricky. I generally use a substitute MainAsync method that the original Main() calls and makes a blocking wait on.
Here comes an example of how it could be done:
class Program
{
static void Main(string[] args)
{
Task mainTask = MainAsync(args);
mainTask.Wait();
// Instead of writing more code here, use the MainAsync-method as your new Main()
}
static async Task MainAsync(string[] args)
{
await Send();
Console.ReadLine();
}
public static async Task Send()
{
for (int i = 0; i < 50; i++)
{
Console.WriteLine(i);
await Task.Delay(2000);
}
}
}

Replace code with timer as opposed to thread sleep

According to this Stack Overflow discussion,using Thread.Sleep() is almost always a bad idea. How would I refactor my code to use a timer instead. I tried to make a start by doing the following:
namespace Engine
{
internal class Program
{
public static DbConnect DbObject = new DbConnect();
System.Timers.Timer timer = new System.Timers.Timer();
// error here
timer.Interval = 2000;
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.Enabled=false;
}
}
but kept getting a cannot resolve symbol error message.
namespace Engine
{
internal class Program
{
public static DbConnect DbObject = new DbConnect();
private static void Main()
{
SettingsComponent.LoadSettings();
while (true)
{
try
{
for (int x = 0; x < 4; x++)
{
GenerateRandomBooking();
}
Thread.Sleep(2000);
GenerateRandomBids();
AllocateBids();
Thread.Sleep(2000);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
}
}
If you are using .Net 4.5 or later, you can use await instead of a Timer.
For example:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Demo
{
public static class Program
{
private static void Main()
{
Console.WriteLine("Generating bids for 30 seconds...");
using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
{
var task = GenerateBids(cancellationTokenSource.Token);
// You can do other work here as required.
task.Wait();
}
Console.WriteLine("\nTask finished.");
}
private static async Task GenerateBids(CancellationToken cancel)
{
while (!cancel.IsCancellationRequested)
{
Console.WriteLine("");
try
{
for (int x = 0; x < 4; x++)
GenerateRandomBooking();
await Task.Delay(2000);
if (cancel.IsCancellationRequested)
return;
GenerateRandomBids();
AllocateBids();
await Task.Delay(2000);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
private static void AllocateBids()
{
Console.WriteLine("AllocateBids()");
}
private static void GenerateRandomBids()
{
Console.WriteLine("GenerateRandomBids()");
}
private static void GenerateRandomBooking()
{
Console.WriteLine("GenerateRandomBooking()");
}
}
}
you can convert your code this one without using Thread.Sleep()
private static void Main()
{
SettingsComponent.LoadSettings();
//while (true)
{
try
{
RaiseRandomBooking(null);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
static void RaiseRandomBooking(object state)
{
for (int x = 0; x < 4; x++)
{
GenerateRandomBooking();
}
System.Threading.Timer tmr = new System.Threading.Timer(RaiseRandomBids, null, 0, 2000);
}
static void RaiseRandomBids(object state)
{
GenerateRandomBids();
AllocateBids();
System.Threading.Timer tmr = new System.Threading.Timer(RaiseRandomBooking, null, 0, 2000);
}

Categories