TaskCompletionSource in async function - c#

I have such a function:
public async Task<bool> DoSomething()
{
var tcs = new TaskCompletionSource<bool>();
// Here is the problem. I need to keep this line because I wait on something asynchronously, but the function must return bool and I can't just return tcs.Task
while(something)
await Task.Delay(100);
someobject.somevent += () => {
// do some sht
tcs.SetResult(true);
}
// it doesn't work
return tcs.Task;
}
It's just a fake code but I have real situation where I need this. I want to keep DoSomething asynchronous but I also want to keep Task.Delay/Sleep in it. How do I do this in not-async function returning just Task?
UPDATE:
THIS WORKS:
class Program
{
static TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
static Task<bool> Test()
{
// tcs = new TaskCompletionSource<bool>();
Task.Factory.StartNew(() =>
{
Console.WriteLine("Waiting...");
Thread.Sleep(5000);
Console.WriteLine("Setting result");
if(tcs.TrySetResult(true))
Console.WriteLine("Result has been set");
});
return tcs.Task;
}
static async Task Test2()
{
Console.WriteLine("Starting awaiting");
var result = await Test();
Console.WriteLine(result.ToString());
}
static void Main(string[] args)
{
Test2();
Console.ReadKey(false);
}
}
and this doesn't
static async Task<bool> Test()
{
// tcs = new TaskCompletionSource<bool>();
Task.Factory.StartNew(() =>
{
Console.WriteLine("Waiting...");
Thread.Sleep(5000);
Console.WriteLine("Setting result");
if(tcs.TrySetResult(true))
Console.WriteLine("Result has been set");
});
return await tcs.Task;
}
what's worse, I have tested it in my windows forms app and awaiting tcs.Task caused weird crash coming from System.Threading....dll

If I understand correctly (it's tricky because your question isn't that easy to follow) you can restate things as follows:
public async Task<bool> DoSomething()
{
var tcs = new TaskCompletionSource<bool>();
someobject.somevent += () => {
// do some sht
tcs.SetResult(true);
}
return await tcs.Task;
}

The whole thing will come out a lot more elegantly if you separate out the logic of turning the event firing into a Task into its own method.
public static Task<bool> WhenSomeEvent(this SomeObject someobject)
{
var tcs = new TaskCompletionSource<bool>();
Action handler = null;
handler = () =>
{
tcs.SetResult(true);
someobject.SomeEvent -= handler;
};
someobject.SomeEvent += handler;
return tcs.Task;
}
This allows you to write the business logic separately, without mixing in all of the logic of translating the event into a Task:
public async Task<bool> DoSomething()
{
while(something)
await Task.Delay(100);
return await someobject.WhenSomeEvent();
}

Related

Wait hub to execute on method to proceed C#

I'm making an integration test and want a method to wait until "on method" is called. In C#.
Something like this:
public async Task CheckMethod(){
var hubConnection = GetHubConnection();
connection.On("OnMethod",async () =>{
Log("First");
}
await connection.InvokeAsync("Subscribe", id);
// Something to wait then proceed
Log("Second");
}
Try this:
public async Task CheckMethod()
{
var tcs = new TaskCompletionSource<object>();
var hubConnection = GetHubConnection();
connection.On("OnMethod",() =>
{
Log("First");
tcs.TrySetResult(null);
}
await connection.InvokeAsync("Subscribe", id);
Log("Second");
await tcs.Task;
Log("Third");
}

Do nest await is the good way to run some async job intervally in parallel?

I have three different job class for different work, each of them needs to run in the background in a specified interval.
Is the following code is good in performance and clean in code style?
if not, how to fix it? I am new to c#, my dev environment is net core 3.1
class Program
{
static void Main(string[] args)
{
Method1();
Method2();
Console.ReadKey();
}
public static async Task Method1()
{
await Task.Run(() =>
{
var run = true;
for (run)
{
var c = await fetchConfigFromDBAsync()
run = c.run;
var interval = c.interval
await JobClass1.RunAsync()
Thread.Sleep(interval)
}
});
}
public static async Task Method2()
{
await Task.Run(() =>
{
for (true)
{
await JobClass2.RunAsync()
Thread.Sleep(new TimeSpan(0, 10, 4))
}
});
}
public static async Task Method3()
{
await Task.Run(() =>
{
for (true)
{
await JobClass3.RunAsync()
Thread.Sleep(new TimeSpan(1, 0, 3))
}
});
}
}
You must avoid Thread.Sleep for theses reasons. You can use Task.Delay() instead.
Then, you don't need to encapsulate your loops into Task.Run() if your async call is really an awaitable function, like sending data, commucicating with database, etc..
If your async function is more like heavy calculations, you can keep it. More explainations here.
And then, it's cleaner to wait all the infinites tasks rather than use Console.ReadLine()
static async Task Main(string[] args)
{
Task m1 = Method1();
Task m2 = Method2();
await Task.WhenAll(new[] { m1, m2 });
}
public static async Task Method2()
{
while (true)
{
await JobClass.RunAsync();
await Task.Delay(3);
}
}
public static async Task Method1()
{
while (true)
{
await JobClass.RunAsync();
await Task.Delay(3);
}
}

Task.Delay unexpected behavior

I have this metod:
public async Task StartAsync(Task process)
{
if (process is null)
{
throw new ArgumentNullException(nameof(process));
}
var loading = ...;
await Task.WhenAll(process, loading).ContinueWith(t => EndProgress());
}
and is called via a command like so:
private async Task TestAsync()
{
await StartAsync(new Task(async () =>
{
//just for testing purpose
await Task.Delay(15000);
}));
}
ExecuteDelegate = async param => await TestAsync();
where ExecuteDelegate is an Action<T> delegate used by command.
Why does the await Task.WhenAll line not waiting those 15 seconds from Task.Dalay?
You need to await the call to StartAsync:
private async Task TestAsync()
{
await StartAsync(new Task(async () =>
{
await Task.Delay(15000);
}));
}
NOTE: You can also simplify your code by not creating the redundant Task:
private async Task TestAsync()
{
await StartAsync(Task.Delay(15000));
}
Or even simpler:
private Task TestAsync()
{
return StartAsync(Task.Delay(15000));
}
You shouldn't use the constructor to create a Task but the static Task.Run method:
private async Task TestAsync()
{
await StartAsync(Task.Run(async () =>
{
//just for testing purpose
await Task.Delay(15000);
}));
}
The task returned by Task.Run can be awaited as expected.

Task.WhenAll doesn't resume

Below code is simplified version what i am trying to do. The code supposed to reach Console.ReadKey() line very quickly but it never happens. Task.WhenAll never resume next line. What is wrong on the code ?
class Program
{
static void Main(string[] args)
{
DoWorkAsync().Wait();
Console.ReadKey();
}
static async Task DoWorkAsync()
{
var block = new ActionBlock<Job>(async (task) =>
{
await task.DoAsync();
});
var jobs = Enumerable.Range(0, 2).Select(i => new Job());
foreach (var job in jobs)
{
await block.SendAsync(job);
}
await Task.WhenAll(jobs.Select(c => c.Completion));
}
public class Job
{
TaskCompletionSource<bool> completionSource = new TaskCompletionSource<bool>();
public Task<bool> Completion { get { return completionSource.Task; } }
public async Task DoAsync()
{
await Task.Delay(100);
completionSource.SetResult(true);
}
}
It will help.
var jobs = Enumerable.Range(0, 2).Select(i => new Job()).ToList();
It happens because of multiple enumeration of jobs
In this line await Task.WhenAll(jobs.Select(c => c.Completion)); you are waiting for new jobs, which are not send to block.

A pattern to pause/resume an async task?

I have a mostly IO-bound continuous task (a background spellchecker talking to a spellcheck server). Sometimes, this task needs to be put on hold and resumed later, depending on the user activity.
While suspend/resume is essentially what async/await does, I've found little information on how to implement the actual pause/play logic for an asynchronous method. Is there a recommended pattern for this?
I've also looked at using Stephen Toub's AsyncManualResetEvent for this, but thought it might be an overkill.
Updated for 2019, I've recently had a chance to revisit this code, below is complete example as a console app (warning: PauseTokenSource needs good unit testing).
Note, in my case, the requirement was that when the consumer-side code (which requested the pause) would continue, the producer-side code should have already reached the paused state. Thus, by the time the UI is ready to reflect the paused state, all background activity is expected to have been already paused.
using System;
using System.Threading.Tasks;
using System.Threading;
namespace Console_19613444
{
class Program
{
// PauseTokenSource
public class PauseTokenSource
{
bool _paused = false;
bool _pauseRequested = false;
TaskCompletionSource<bool> _resumeRequestTcs;
TaskCompletionSource<bool> _pauseConfirmationTcs;
readonly SemaphoreSlim _stateAsyncLock = new SemaphoreSlim(1);
readonly SemaphoreSlim _pauseRequestAsyncLock = new SemaphoreSlim(1);
public PauseToken Token { get { return new PauseToken(this); } }
public async Task<bool> IsPaused(CancellationToken token = default(CancellationToken))
{
await _stateAsyncLock.WaitAsync(token);
try
{
return _paused;
}
finally
{
_stateAsyncLock.Release();
}
}
public async Task ResumeAsync(CancellationToken token = default(CancellationToken))
{
await _stateAsyncLock.WaitAsync(token);
try
{
if (!_paused)
{
return;
}
await _pauseRequestAsyncLock.WaitAsync(token);
try
{
var resumeRequestTcs = _resumeRequestTcs;
_paused = false;
_pauseRequested = false;
_resumeRequestTcs = null;
_pauseConfirmationTcs = null;
resumeRequestTcs.TrySetResult(true);
}
finally
{
_pauseRequestAsyncLock.Release();
}
}
finally
{
_stateAsyncLock.Release();
}
}
public async Task PauseAsync(CancellationToken token = default(CancellationToken))
{
await _stateAsyncLock.WaitAsync(token);
try
{
if (_paused)
{
return;
}
Task pauseConfirmationTask = null;
await _pauseRequestAsyncLock.WaitAsync(token);
try
{
_pauseRequested = true;
_resumeRequestTcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
_pauseConfirmationTcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
pauseConfirmationTask = WaitForPauseConfirmationAsync(token);
}
finally
{
_pauseRequestAsyncLock.Release();
}
await pauseConfirmationTask;
_paused = true;
}
finally
{
_stateAsyncLock.Release();
}
}
private async Task WaitForResumeRequestAsync(CancellationToken token)
{
using (token.Register(() => _resumeRequestTcs.TrySetCanceled(), useSynchronizationContext: false))
{
await _resumeRequestTcs.Task;
}
}
private async Task WaitForPauseConfirmationAsync(CancellationToken token)
{
using (token.Register(() => _pauseConfirmationTcs.TrySetCanceled(), useSynchronizationContext: false))
{
await _pauseConfirmationTcs.Task;
}
}
internal async Task PauseIfRequestedAsync(CancellationToken token = default(CancellationToken))
{
Task resumeRequestTask = null;
await _pauseRequestAsyncLock.WaitAsync(token);
try
{
if (!_pauseRequested)
{
return;
}
resumeRequestTask = WaitForResumeRequestAsync(token);
_pauseConfirmationTcs.TrySetResult(true);
}
finally
{
_pauseRequestAsyncLock.Release();
}
await resumeRequestTask;
}
}
// PauseToken - consumer side
public struct PauseToken
{
readonly PauseTokenSource _source;
public PauseToken(PauseTokenSource source) { _source = source; }
public Task<bool> IsPaused() { return _source.IsPaused(); }
public Task PauseIfRequestedAsync(CancellationToken token = default(CancellationToken))
{
return _source.PauseIfRequestedAsync(token);
}
}
// Basic usage
public static async Task DoWorkAsync(PauseToken pause, CancellationToken token)
{
try
{
while (true)
{
token.ThrowIfCancellationRequested();
Console.WriteLine("Before await pause.PauseIfRequestedAsync()");
await pause.PauseIfRequestedAsync();
Console.WriteLine("After await pause.PauseIfRequestedAsync()");
await Task.Delay(1000);
}
}
catch (Exception e)
{
Console.WriteLine("Exception: {0}", e);
throw;
}
}
static async Task Test(CancellationToken token)
{
var pts = new PauseTokenSource();
var task = DoWorkAsync(pts.Token, token);
while (true)
{
token.ThrowIfCancellationRequested();
Console.WriteLine("Press enter to pause...");
Console.ReadLine();
Console.WriteLine("Before pause requested");
await pts.PauseAsync();
Console.WriteLine("After pause requested, paused: " + await pts.IsPaused());
Console.WriteLine("Press enter to resume...");
Console.ReadLine();
Console.WriteLine("Before resume");
await pts.ResumeAsync();
Console.WriteLine("After resume");
}
}
static async Task Main()
{
await Test(CancellationToken.None);
}
}
}
AsyncManualResetEvent is exactly what you need, considering how messy your current code is. But a slightly better solution would be to use another approach from Stephen Toub: PauseToken. It works similarly to AsyncManualResetEvent, except its interface is made specifically for this purpose.
All the other answers seem either complicated or missing the mark when it comes to async/await programming by holding the thread which is CPU expensive and can lead to deadlocks. After lots of trial, error and many deadlocks, this finally worked for my high usage test.
var isWaiting = true;
while (isWaiting)
{
try
{
//A long delay is key here to prevent the task system from holding the thread.
//The cancellation token allows the work to resume with a notification
//from the CancellationTokenSource.
await Task.Delay(10000, cancellationToken);
}
catch (TaskCanceledException)
{
//Catch the cancellation and it turns into continuation
isWaiting = false;
}
}
it is works for me
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TaskTest2
{
class Program
{
static ManualResetEvent mre = new ManualResetEvent(false);
static void Main(string[] args)
{
mre.Set();
Task.Factory.StartNew(() =>
{
while (true)
{
Console.WriteLine("________________");
mre.WaitOne();
}
} );
Thread.Sleep(10000);
mre.Reset();
Console.WriteLine("Task Paused");
Thread.Sleep(10000);
Console.WriteLine("Task Will Resume After 1 Second");
Thread.Sleep(1000);
mre.Set();
Thread.Sleep(10000);
mre.Reset();
Console.WriteLine("Task Paused");
Console.Read();
}
}
}
Ok, maybe this deserves an answer, but I'm not so familiar with C# and I don't have MonoDevelop here, and it's 3 o' clock AM, so please have pity.
I'm suggesting something like this
class Spellchecker
{
private CancellationTokenSource mustStop = null;
private volatile Task currentTask = null;
//TODO add other state variables as needed
public void StartSpellchecker()
{
if (currentTask != null)
{
/*
* A task is already running,
* you can either throw an exception
* or silently return
*/
}
mustStop = new CancellationTokenSource();
currentTask = SpellcheckAsync(mustStop.Token);
currentTask.Start();
}
private async Task SpellcheckAsync(CancellationToken ct)
{
while (!ct.IsCancellationRequested))
{
/*
* TODO perform spell check
* This method must be the only one accessing
* the spellcheck-related state variables
*/
}
currentTask = null;
}
public async Task StopSpellchecker()
{
if (currentTask == null)
{
/*
* There is no task running
* you can either throw an exception
* or silently return
*/
}
else
{
/*
* A CancelAfter(TimeSpan) method
* is also available, which might interest you
*/
mustStop.Cancel();
//Remove the following lines if you don't want to wait for the task to actually stop
var task = currentTask;
if (task != null)
{
await task;
}
}
}
}

Categories

Resources