Can I wrap Task.Run under another Task.Run()? - c#

I have a method HandleAcceptedConnection that is under Task.Run() that i want to run asynchronously(in another separate thread). I tried declaring HandleAcceptedConnection as async method and dont call await but it doesnt seem to run asynchronously. I can confirm that I can have Task.Run()(by watching the thread id) under another Task.Run() but is that recommended?
private async void Start_Click(object sender, RoutedEventArgs e)
{
var task = Task.Run(() =>
{
while (isContinue)
{
var handler = listener.Accept();
// handle connection
Log("Before");
Log("ThreadId Accept " + Thread.CurrentThread.ManagedThreadId);
// i want to run method below asynchronously. i want to
// wrap it under Task.Run() but i am already under
// Task.Run(). i set HandleAcceptedConnection as async. i thought by not
// calling await on HandleAcceptedConnection, HandleAcceptedConnection
// is asynchronous
HandleAcceptedConnection(handler);
Log("After");
isContinue = true;
}
});
await task;
}
private async Task HandleAcceptedConnection(Socket handler)
{
Log("ThreadId HandleAcceptedConnection " + Thread.CurrentThread.ManagedThreadId);
Log("Under HandleAcceptedConnection");
Thread.Sleep(10000);
}
When i run this, logs says
Before
Under HandleAcceptedConnection
After
i want
Before
After
Under HandleAcceptedConnection
i want HandleAcceptedConnection to be run asynchronously. Should i wrap it under another Task.Run or it is already asynchronous?

Did you try
private async Task HandleAcceptedConnection(Socket handler)
{
Thread.Sleep(1000);
Log("Under HandleAcceptedConnection");
}
Because doing something on another thread doesn't mean it'll be delayed.

You should be using AcceptTcpClientAsync, then you won't need extra threads. Check this answer for an example. Don't use a synchronous API when there is a naturally asynchronous version of it available.
Updated to address the comment. Nothing prevents you from using Task.Run from inside Task.Run, you code might look like this (untested):
private async void Start_Click(object sender, RoutedEventArgs e)
{
var connectionTasks = new List<Task>();
Func<Task> handleConnection = async () =>
{
var connectionTask = Task.Run(() => HandleAcceptedConnection(handler));
connectionTasks.Add(connectionTask);
await connectionTask;
connectionTasks.Remove(connectionTask);
};
var task = Task.Run(() =>
{
while (isContinue)
{
var handler = listener.Accept();
// handle connection
Log("Before");
Log("ThreadId Accept " + Thread.CurrentThread.ManagedThreadId);
var connectionTask = handleConnection();
Log("After");
isContinue = true;
}
});
await task;
}

Related

C# Task wait and timeout

I am trying to use the following technique to be able to have a worker task executing some operations, with a 10 sec timeout and without blocking the application.
internal void ReadAll()
{
var data = new byte[1];
Task.Factory.StartNew(() =>
{
var ct = new CancellationTokenSource();
var ReadAllTask = Task.Factory.StartNew(() =>
{
// Read all information
// [omit communication exchange via COM port]
ct.Cancel();
}, ct.Token);
// First thread waiting 10s for the worker to finish
ReadAllTask.Wait(10000, ct.Token);
if (ReadAllTask.Status == TaskStatus.RanToCompletion)
{
ReadAllComplete?.Invoke(true);
}
else
{
ct.Cancel();
ReadAllComplete?.Invoke(false);
}
});
}
This method is called by pressing a button. It seems to me that in debug configuration works properly, but not in release configuration where the "first thread" never reach the wait and no event is thrown.
Your code could be a lot simpler than current version. Easiest way to make a non-blocking method for event is to mark it with async keyword and use the await keyword to start the asynchronous read operation from SerialPort.BaseStream property.
Also, CancellationTokenSource could be created with time, after that it get cancelled automatically, and the right way to cancel is to call CancellationToken.ThrowIfCancellationRequested method. async/await mechanism will invoke the event in UI context, so code could be something like this:
// async void is a recommended way to use asynchronous event handlers
private async void btnReadAll_Click(object sebder, EventArgs e)
{
var data = new byte[2];
// cancel source after 10 seconds
var cts = new CancellationTokenSource(10000);
// Read all information
// [omit communication exchange via COM port]
// async operation with BaseStream
var result = await SerialPort.BaseStream.ReadAsync(data, 0, 2, cts.Token);
/*
* if you can't use the BaseStream methods, simply call this method here
* cts.Token.ThrowIfCancellationRequested();
*/
// this code would run only if everything is ok
// check result here in your own way
var boolFlag = result != null;
ReadAllComplete?.Invoke(boolFlag);
}
Here's just a quick rewrite to remove the event and wrap what appears to be a synchronous IO API in an async one. If at all possible you should switch to a true async API and drop the Task.Run.
private CancellationTokenSource cts;
public async void MyButtonhandler(object sender, EventArgs e) {
cts = new CancellationTokenSource();
try {
var result = await Task.Run(() => ReadAll(cts));
if (result) {
//success
} else {
//failure
}
} catch (TaskCanceledException ex) {
}
}
internal async Task<bool> ReadAll(CancellationTokenSource cts) {
byte[] data = new byte[1];
var timeout = TimeSpan.FromSeconds(10);
var ReadAllTask = Task.Run(() => {
// Read all information
// [omit communication exchange via COM port]
}, cts.Token);
if (await Task.WhenAny(ReadAllTask, Task.Delay(timeout)) == ReadAllTask) {
return true;
}
cts.Cancel();
return false;
}
Reading comments and answers to my question I learned a couple of useful things that solve my problem:
CancellationTokenSource can have an implicit timeout
use Task.Run instead Task.Factory.StartNew
don't need to cancel the task, the cts will do the work
Now my code is simpler and it works:
private void Read_All_Button_Click(object sender, RoutedEventArgs e)
{
// Start timedout task that will send all necessary commands
CancellationTokenSource cts = new CancellationTokenSource(10000);
Task.Run(() =>
{
oCommandSets.ReadAll(cts);
}, cts.Token);
}
and
internal void ReadAll(CancellationTokenSource cts)
{
// [communication]
if (cts.IsCancellationRequested)
{
ReadAllComplete?.Invoke(false);
}
else
{
ReadAllComplete?.Invoke(true);
}
}
In any case I need to learn more about multithreading.

Executing multiple tasks asynchronously in C#

Environment
Windows 7
Visual Studio
C#
What I'm trying to do
I'm trying to build an app to evaluate company products. For security, the description below is made abstract to some extent.
What this app does is changing a certain parameter in the product and see how a certain value of the product changes. So I need to do two things.
Change the parameter at a certain interval
Display the value in a textbox at a certain interval
The diagram is like this.
These tasks should be repeated until a cancel button is pressed.
The UI has these controls:
button1 : start button
button2 : cancel button
textbox1 : to show values obtained from the device
So here is the code I wrote.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
CancellationTokenSource cts = new CancellationTokenSource();
private async void button1_Click(object sender, EventArgs e)
{
await Task1();
await Task2();
}
private async Task Task1()
{
while (!cts.IsCancellationRequested)
{
Thread.Sleep(500);
ChangeParameter(0);
Thread.Sleep(1000);
ChangeParameter(10);
Thread.Sleep(500);
ChangeParameter(0);
}
}
private void ChangeParameter(double param)
{
// change device paremeter
Console.WriteLine("devicep parameter changed : " + param);
}
private async Task Task2()
{
while (!cts.IsCancellationRequested)
{
Thread.Sleep(100);
int data = GetDataFromDevice();
UpdateTextBoxWithData(data);
}
cts.Token.ThrowIfCancellationRequested();
}
private int GetDataFromDevice()
{
//pseudo code
var rnd = new Random();
return rnd.Next(100);
}
private void UpdateTextBoxWithData(int data)
{
textBox1.AppendText(data.ToString() + "\n");
// debug
Console.WriteLine("data : " + data);
}
private void button2_Click(object sender, EventArgs e)
{
cts.Cancel();
}
}
Issues
However, there are two issues in this code.
UI freezes.
Task2 is never executed.
The second issue is derived from await since it executes tasks one by one. I could have used Task.Run() but this doesn't allow adding values to textBox since it's different from the UI thread.
How can I solve these issues? Any help would be appreciated.
First of all, async methods can be illusive as they won't turn your methods magically asynchronous. Instead, you can consider an async method as a setup for a state machine (see a detailed explanation here), where you schedule the chain of operations by the await calls.
For that reason, your async methods must execute as fast as possible. Do not do any blocking operation in such a setup method. If you have a blocking operation, which you want to execute in the async method, schedule it by an await Task.Run(() => MyLongOperation()); call.
So for example this will return immediately:
private async Task Task1()
{
await Task.Run(() =>
{
while (!cts.IsCancellationRequested)
{
Thread.Sleep(500);
ChangeParameter(0);
Thread.Sleep(1000);
ChangeParameter(10);
Thread.Sleep(500);
ChangeParameter(0);
}
}
}
A small remark: others may suggest to use Task.Delay instead of Thread.Sleep. I would say that use Task.Delay only if it is the part of the configuration of your state machine. But if the delay is intended to be used as a part of the long-lasting operation, which you don't want to split up, you can simply stay at the Thread.Sleep.
Finally, a remark for this part:
private async void button1_Click(object sender, EventArgs e)
{
await Task1();
await Task2();
}
This configures your tasks to be executed after each other. If you want to execute them parallel, do it like this:
private async void button1_Click(object sender, EventArgs e)
{
Task t1 = Task1();
Task t2 = Task2();
await Task.WhenAll(new[] { t1, t2 });
}
Edit: An extra note for long-lasting tasks: By default, Task.Run executes the tasks on pool threads. Scheduling too many parallel and long lasting tasks might cause starvation and the whole application may freeze for long seconds. So for long-lasting operation you might want to use Task.Factory.StartNew with TaskCreationOptions.LongRunning option instead of Task.Run.
// await Task.Run(() => LooongOperation(), token);
await Task.Factory.StartNew(() => LooongOperation(), token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
The problem is you not using await in your tasks so they executing synchronously.
You should use something like this to maintain your UI responsive (NOTE this is not production code, I'm just showing an idea):
private void button1_Click(object sender, EventArgs e)
{
try
{
await Task.WhenAll(Task1(cts.Token), Task2(cts.Token));
}
catch (TaskCancelledException ex)
{
}
}
private async Task Task1(CancellationToken token)
{
while (true)
{
token.ThrowIfCancellationRequested();
await Task.Delay(500, token); // pass token to ensure delay canceled exactly when cancel is pressed
ChangeParameter(0);
await Task.Delay(1000, token);
ChangeParameter(10);
await Task.Delay(500, token);
ChangeParameter(0);
}
}
private async Task Task2(CancellationToken token)
{
while (true)
{
token.ThrowIfCancellationRequested();
await Task.Delay(100, token);
int data = await Task.Run(() => GetDataFromDevice()); //assuming this could be long running operation it shouldn't be on ui thread
UpdateTextBoxWithData(data);
}
}
Basically, when you need to run something on background you should wrap that in Task.Run() and then await for result. Simply adding async to your method won't make this method asynchronous.
To make your code clearer, I suggest you to move methods like GetDataFromDevice or ChangeParameter to services layer. Also, take a look at IProgress as comments suggests to update your UI according to progress of some process.
There are many issues with this code:
async/await doesn't make the code asynchronous automagically. It allows you to await the results of already asynchronous operations. If you want to run something in the background that isn't already asynchronous, you need to use Task.Run or a similar method to start a Task.
await returns execution to the original synchronization context. In this case, the UI thread. By using Thread.Sleep, you are freezing the UI thread
You can't update the UI from another thread and that goes for Tasks too. You can use the IProgress interface though to report progress. A lot of BCL classes use this interface, just like CancellationToken
Maxim Kosov already cleaned up the code and shows how to properly use async/await and Task.Run, so I'll just post how to use IProgress< T> and its impelementation, Progress< T>
IProgress is used to publich a progress update with the IProgress< T>.Report method. Its default implementation, Progress, raises the ProgressChanged event and/or calls the Action<T> passed to its constructor, on the UI thread. Specifically, on the synchronization context captured when the class was created.
You can create a progress object in your constructor or your button click event, eg
private async void button1_Click(object sender, EventArgs e)
{
var progress=new Progress<int>(data=>UpdateTextBoxWithData(data));
//...
//Allow for cancellation of the task itself
var token=cts.Token;
await Task.Run(()=>MeasureInBackground(token,progress),token);
}
private async Task MeasureInBackground(CancellationToken token,IProgress<int> progress)
{
while (!token.IsCancellationRequested)
{
await Task.Delay(100,token);
int data = GetDataFromDevice();
progress.Report(data);
}
}
Note that using Thread.Sleep inside a task is not a good idea because it wastes a threadpool thread doing nothing. It's better to use await Task.Delay() which requires that the signature of the method change to async Task. There is a Task.Run(Func) overload just for this purpose.
The method is a bit different from Maxim Kosov's code to show that IProgress really communicates across threads. IProgress can handle complex classes, so you could return both a progress percentage and a message, eg:
private async Task MeasureInBackground(CancellationToken token,IProgress<Tuple<int,string>> progress)
{
while(!token.IsCancellationRequested)
{
await Task.Delay(100,token);
int data = GetDataFromDevice();
progress.Report(Tuple.Create(data,"Working"));
}
progress.Report(Tuple.Create(-1,"Cancelled!"));
}
Here I'm just being lazy and return a Tuple<int,string>. A specialized progress class would be more appropriate in production code.
The advantage of using an Action is that you don't need to manage event handlers and the objects are local to the async method. Cleanup is performed by .NET itself.
If your device API provides truly asynchronous calls, you don't need Task.Run. This means that you don't have to waste a Task in a tigh loop, eg:
private async Task MeasureInBackground(CancellationToken token,IProgress<Tuple<int,string>> progress)
{
while(!token.IsCancellationRequested)
{
await Task.Delay(100, token);
int data = await GetDataFromDeviceAsync();
progress.Report(Tuple.Create(data,"Working"));
}
progress.Report(Tuple.Create(-1,"Cancelled!"));
}
Most drivers perform IO tasks using an OS feature called completion ports, essentially callbacks that are called when the driver completes an operation. This way they don't need to block while waiting for a network, database or file system response.
EDIT
In the last example, Task.Run is no longer needed. Just using await would be enough:
await MeasureInBackground(token,progress);

How to serialize async/await?

Let's suppose I have this simple snippet:
async void button_Click(object sender, RoutedEventArgs e)
{
await Task.Factory.StartNew(() =>
{
Console.WriteLine("start");
Thread.Sleep(5000);
Console.WriteLine("end");
});
}
Obviously, everytime I push that button a new task is started even when a previous task still runs. How would I postpone any new task until all previous tasks have finished?
Some more details:
In the example above, each new task is identical to the task before. However, in the original context the sequence of tasks matters: Parameters may change (I could "simulate" it by using DateTime.Now.Ticks).
The tasks should be executed in the order they are "registered". Specificly, my program will talk to a serial device. I've done this before with a background thread utilizing a BlockingCollection. However, this time there's a strict request/response-protocol and I'd like to use async/await if it is possible.
Possible solution:
I could imagine creating tasks and storing them in a list. But how would I execute the tasks with respect to the requirements? Or should I return to the thread-based solution I have used before?
I recommend using a SemaphoreSlim for synchronization. However, you want to avoid Task.Factory.StartNew (as I explain on my blog), and also definitely avoid async void (as I explain in the MSDN article).
private SemaphoreSlim _mutex = new SemaphoreSlim(1);
async void button_Click(object sender, RoutedEventArgs e)
{
await Task.Run(async () =>
{
await _mutex.WaitAsync();
try
{
Console.WriteLine("start");
Thread.Sleep(5000);
Console.WriteLine("end");
}
finally
{
_mutex.Release();
}
});
}
You could wait on a SemaphoreSlim asynchronously and release it once the job is done. Don't forget to configure the semaphore initialcount to 1.
private static SemaphoreSlim semaphore = new SemaphoreSlim(1);
private async static void DoSomethingAsync()
{
await semaphore.WaitAsync();
try
{
await Task.Factory.StartNew(() =>
{
Console.WriteLine("start");
Thread.Sleep(5000);
Console.WriteLine("end");
});
}
finally
{
semaphore.Release();
}
}
private static void Main(string[] args)
{
DoSomethingAsync();
DoSomethingAsync();
Console.Read();
}
What about trying the Dataflow.ActionBlock<T> with the (default) max degree of parallelism of 1. This way you don't need to worry about any of the thread safety / locking concerns.
It could look something like:
...
var _block = new ActionBlock<bool>(async b =>
{
Console.WriteLine("start");
await Task.Delay(5000);
Console.WriteLine("end");
});
...
async void button_Click(object sender, RoutedEventArgs e)
{
await _block.SendAsync(true);
}
You could also setup the ActionBlock to receive a Task or Func<Task>, and simply run / await this input. Which would allow multiple operations to be queued and awaited from different sources.
I might be missing something, but I don't think SemaphoreSlim is needed for the OP's scenario. I'd do it the following way. Basically, the code just await the previous pending instance of the task before continuing (no exception handling for clarity):
// the current pending task (initially a completed stub)
Task _pendingTask = Task.FromResult<bool>(true);
async void button_Click(object sender, RoutedEventArgs e)
{
var previousTask = _pendingTask;
_pendingTask = Task.Run(async () =>
{
await previousTask;
Console.WriteLine("start");
Thread.Sleep(5000);
Console.WriteLine("end");
});
// the following "await" is optional,
// you only need it if you have other things to do
// inside "button_Click" when "_pendingTask" is completed
await _pendingTask;
}
[UPDATE] To address the comment, here's a thread-safe version, when button_Click can be called concurrently:
Task _pendingTask = Task.FromResult<bool>(true);
object _pendingTaskLock = new Object();
async void button_Click(object sender, RoutedEventArgs e)
{
Task thisTask;
lock (_pendingTaskLock)
{
var previousTask = _pendingTask;
// note the "Task.Run" lambda doesn't stay in the lock
thisTask = Task.Run(async () =>
{
await previousTask;
Console.WriteLine("start");
Thread.Sleep(5000);
Console.WriteLine("end");
});
_pendingTask = thisTask;
}
await thisTask;
}

Multiple worker threads vs One worker with async/await

My code currently has the following 10 worker threads. Each worker thread continues polling a job from the queue and then process the long running job.
for (int k=0; k<10; k++)
{
Task.Factory.StartNew(() => DoPollingThenWork(), TaskCreationOptions.LongRunning);
}
void DoPollingThenWork()
{
while (true)
{
var msg = Poll();
if (msg != null)
{
Thread.Sleep(3000); // process the I/O bound job
}
}
}
I am refactoring the underlying code to use async/await pattern. I think I can rewrite the above code to the followings. It uses one main thread that keeps creating the async task, and use SemaphoreSlim to throttle the number of concurrent tasks to 10.
Task.Factory.StartNew(() => WorkerMainAsync(), TaskCreationOptions.LongRunning);
async Task WorkerMainAsync()
{
SemaphoreSlim ss = new SemaphoreSlim(10);
while (true)
{
await ss.WaitAsync();
Task.Run(async () =>
{
await DoPollingThenWorkAsync();
ss.Release();
});
}
}
async Task DoPollingThenWorkAsync()
{
var msg = Poll();
if (msg != null)
{
await Task.Delay(3000); // process the I/O-bound job
}
}
Both should behave the same. But I think the second options seems better because it doesn't block the thread. But the downside is I can't do Wait (to gracefully stop the task) since the task is like fire and forget. Is the second option the right way to replace the traditional worker threads pattern?
When you have code that's asynchronous, you usually have no reason to use Task.Run() (or, even worse, Task.Factory.StartNew()). This means that you can change your code to something like this:
await WorkerMainAsync();
async Task WorkerMainAsync()
{
SemaphoreSlim ss = new SemaphoreSlim(10);
while (true)
{
await ss.WaitAsync();
// you should probably store this task somewhere and then await it
var task = DoPollingThenWorkAsync();
}
}
async Task DoPollingThenWorkAsync(SemaphoreSlim semaphore)
{
var msg = Poll();
if (msg != null)
{
await Task.Delay(3000); // process the I/O-bound job
}
// this assumes you don't have to worry about exceptions
// otherwise consider try-finally
semaphore.Release();
}
Usually you don't use async/await inside a CPU-bound task. The method that starts such a task (WorkerMainAsync) can use async/await, but you should be tracking pending tasks:
async Task WorkerMainAsync()
{
SemaphoreSlim ss = new SemaphoreSlim(10);
List<Task> trackedTasks = new List<Task>();
while (DoMore())
{
await ss.WaitAsync();
trackedTasks.Add(Task.Run(() =>
{
DoPollingThenWorkAsync();
ss.Release();
}));
}
await Task.WhenAll(trackedTasks);
}
void DoPollingThenWorkAsync()
{
var msg = Poll();
if (msg != null)
{
Thread.Sleep(2000); // process the long running CPU-bound job
}
}
Another exercise would be to remove tasks from trackedTasks as they are finishing. For example, you could use ContinueWith to remove a finished tasks (in this case, remember to use lock to protect trackedTasks from simultaneous access).
If you really need to use await inside DoPollingThenWorkAsync, the code wouldn't change a lot:
trackedTasks.Add(Task.Run(async () =>
{
await DoPollingThenWorkAsync();
ss.Release();
}));
Note that in this case, you'd be dealing with a nested task here for the async lambda, which Task.Run will automatically unwrap for you.

How can I timeout a task without it blocking the UI thread?

I have some code that calls a method to download a file:
private async Task DownloadFile()
{
WebClient client = new WebClient();
var downloadTask =
Task.Run(
() =>
client.DownloadFile("http://www.worldofcats.com/bigkitty.zip",
"c:\\cats\\"
);
await downloadTask;
}
To invoke this method, I do this:
var downloadTask = DownloadFile();
await downloadTask;
Since it's part of a forms app, this causes no issue while it downloads with the UI being unresponsive. The only problem is, the DownloadFile method has no timeout, and sometimes it might go wrong or hang, so I need to put a timeout in.
If I use Task.Wait(x); then it blocks the UI thread. I think I can possibly use await Task.WhenAny(downloadTask, () => Thread.Sleep(50000)); but I am not sure if this is the best way.
So my question is, what should I do to solve this, and how can I cleanup my task if it's forcibly terminated? (Or do I have to worry about that?)
Older solution, not feasible for tasks that are not cancellation-aware
You should pass a CancellationToken:
private async Task DownloadFile()
{
WebClient client = new WebClient();
using(var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60))
{
var downloadTask =
Task.Run(
() =>
client.DownloadFile("http://www.worldofcats.com/bigkitty.zip",
"c:\\cats\\"),
cts.Token
);
await downloadTask;
}
}
Now when you await DownloadFile() you can wrap it in a try/catch block to catch the TaskCanceledException (or OperationCanceledException):
try
{
await DownloadFile();
}
catch(TaskCanceledException)
{
//Timeout!
}
[EDIT]
As was noticed in comments, you can't cancel a task that's not cancellation-aware - somehow I forgot about that (boo!). But no worries, you can fix that by using DownloadFileTaskAsync and CancelAsync, so you don't even need cancellation token:
var downloadTask = client.DownloadFileTaskAsync("http://www.worldofcats.com/bigkitty.zip",
"c:\\cats\\");
var timerTask = Task.Delay(TimeSpan.FromSeconds(60));
await Task.WhenAny(downloadTask, timerTask);
client.CancelAsync(); // This does nothing if there's no operation in progress, as noted in documentation
check is out:
private static async void Test()
{
var source = new CancellationTokenSource();
var watcher = Task.Delay(TimeSpan.FromSeconds(4), source.Token);
var downloadTask = Task.Factory.StartNew(() =>
{
//.. Simulating a long time task
Thread.Sleep(TimeSpan.FromSeconds(10));
},
source.Token);
await Task.Run(() => { Task.WaitAny(watcher, downloadTask); });
source.Cancel();
if (!downloadTask.IsCompleted)
Console.WriteLine("Time out!");
else
Console.WriteLine("Done");
}

Categories

Resources