SendOrPostCallback behaviour - c#

SendOrPostCallback Represents a method to be called when a message is to be dispatched to a synchronization context. In first case SendOrPostCallback points on async method that i guess should be executed synchronously. What will happen if Delegate will point on async delegate? How behaviour should change?
Before changes:
public class ViewModel
{
public ViewModel()
{
SynchronizationContext.Current.Post(new SendOrPostCallback(SomeMethods), null);
}
private async void SomeMethods(object obj)
{
await Foo(obj);
}
private async Task Foo(object obj)
{
bool Canceled = false;
while (!Canceled)
{
await Task.Delay(3000);
//...
}
}
}
After changes:
public class ViewModelImproved
{
public ViewModelImproved()
{
SynchronizationContext.Current.Post(new SendOrPostCallback(async (obj) => { await SomeMethods(obj); }), null);
}
private async Task SomeMethods(object obj)
{
await Foo(obj);
}
private async Task Foo(object obj)
{
bool Canceled = false;
while (!Canceled)
{
await Task.Delay(3000);
}
//...
}
}

There's no substantial difference. In the first code, there's an async void, and in the second code, there's an async void. In the second code, the async void is hidden within a lambda expression.
Generally, you should avoid async void. In this particular case, it may be OK, since you're dealing with a UI context.

Related

Action vs EventHandler vs Func for async/awaited method calls

I have async/awaited methods in my Connected anonymous method implementation and I wonder which one of these is the best to use in my case. I put examples below and added information according to what I know. Are my statements correct? Anything else important that I'm missing?
EventHandler
The only disadvantage to me is that the anonymous method is basically async void. Yes, we can have async calls, but they will be kinda "fire and forget/crash".
public class Test
{
public event EventHandler<ConnectEventArgs>? Connected;
public ValueTask StartAsync()
{
Connected?.Invoke(this, new ConnectEventArgs(...))
}
}
var test = new Test();
test.Connected += async (sender, e) => // private async void OnConnect(object? sender, ConnectEventArgs e) => BAD
{
await SendAsync(); // fire and forget
};
await test.StartAsync();
Func
What I notice is that we can have async calls without having to worry about async void. I think that's the only good way to have async/awaited method calls. Isn't it?
public class Test
{
public event Func<Task>? Connected;
public ValueTask StartAsync()
{
await (Connected?.Invoke() ?? Task.CompletedTask).ConfigureAwait(false);
}
}
var test = new Test();
test.Connected += async () => // private async Task OnConnect()
{
await SendAsync(); // Should be okay?
};
await test.StartAsync();
Action
The only difference between Action and EventHandler that comes into my mind is that we don't pass the sender and we don't have to create EventArgs classes.
public class Test
{
public event Action<bool>? Connected;
public ValueTask StartAsync()
{
Connected?.Invoke(true);
}
}
var test = new Test();
test.Connected += async (boolean) => // private void OnConnect(bool boolean)
{
await SendAsync(); // fire and forget
};
await test.StartAsync();

How to do WaitUntil in async?

All I can find online talking about await in async is teaching me how to "wait for a certain period of time", which is not what I want.
I want it to wait until a certain condition is met.
Just lile
yield return new WaitUntil(()=>conditionIsMet);
in Coroutine.
I want to do something like
await Task.WaitUntil(()=>conditionIsMet);
Is such thing possible?
Could somebody please be so kind and help me out?
Thank you very much for your help.
you can use UniTask Plugin which Provides an efficient allocation free async/await integration for Unity.
it has a lot of features and it is easy to use.
First download it from here,then include it in your project and use it like this :
using Cysharp.Threading.Tasks;
using UnityEngine;
public class Test : MonoBehaviour
{
bool _condition;
// Start is called before the first frame update
void Start()
{
_ = ProcessAsyncTask();
}
public async UniTask ProcessAsyncTask()
{
await UniTask.WaitUntil( ()=> ConditionResult());
}
public bool ConditionResult()
{
// _condition
return _condition;
}
}
Wouldn't this basically simply be something like
public static class TaskUtils
{
public static Task WaitUntil(Func<bool> predicate)
{
while (!predicate()) { }
}
}
though for the love of your CPU I would actually rather give your task certain sleep intervals like
public static class TaskUtils
{
public static async Task WaitUntil(Func<bool> predicate, int sleep = 50)
{
while (!predicate())
{
await Task.Delay(sleep);
}
}
}
and now you could e.g. use something like
public class Example : MonoBehaviour
{
public bool condition;
private void Start()
{
Task.Run(async ()=> await YourTask());
}
public async Task YourTask()
{
await TaskUtils.WaitUntil(IsConditionTrue);
// or as lambda
//await TaskUtils.WaitUntil(() => condition);
Debug.Log("Hello!");
}
private bool IsConditionTrue()
{
return condition;
}
}
you can create your own ConsitionCheker method, and use it in your MainFunction that gets a boolian which awaits for your condition result
public async Task<bool> CheckCondition()
{
While(true){
// check the condition
if(mycondition is true)
break;
}
return true;
}
public void Do(bool consitionResult)
{
if(consitionResult){
// codes
}
}
usage :
public async void Test()
{
// waiting for condition met
// pass the condition result
Do(await CheckCondition());
}

How to catch that Task.Delay() canceled and do a callback?

Lets say I have a worker class.
public sealed class Worker : IDisposable
{
private bool _isRunning;
private CancellationTokenSource _cts;
private readonly Action _action;
private readonly int _millisecondsDelay;
private readonly object _lock = new object();
public Worker(Action action, int millisecondsDelay)
{
_action = action;
_millisecondsDelay = millisecondsDelay = 5000;
}
public void Start()
{
lock (_lock)
{
if (!_isRunning)
{
_isRunning = true;
Run();
}
}
}
public void Cancel()
{
lock (_lock)
{
if (_isRunning) _cts.Cancel();
}
}
private void Run()
{
using (_cts) _cts = new CancellationTokenSource();
Task.Run(async () => { await DoAsync(_cts.Token); });
}
private async Task DoAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
//Log.Message1("____REFRESHING STATUS____");
_action();
await Task.Delay(_millisecondsDelay, cancellationToken);
}
//this code is unreachable
lock (_lock)
{
_isRunning = false;
}
}
public void Dispose()
{
try
{
_cts?.Cancel();
}
finally
{
if (_cts != null)
{
_cts.Dispose();
_cts = null;
}
}
}
}
The problem is the code _isRunning = false; is unreachable. I mean more likely when a caller call Cancel method the worker will be awaiting Task.Delay. So how I can call smth(here it's _isRunning = false;) after my Task will be canceled ? In other words I need to be sure that my worker is not running(it's not the cancelled state)
To answer your literal question, you can use a finally block:
private async Task DoAsync(CancellationToken cancellationToken)
{
try
{
while (!cancellationToken.IsCancellationRequested)
{
//Log.Message1("____REFRESHING STATUS____");
_action();
await Task.Delay(_millisecondsDelay, cancellationToken);
}
}
finally
{
lock (_lock)
{
_isRunning = false;
}
}
}
But I have some concerns about this "worker" approach:
I'm not a huge fan of the fire-and-forget inside Run. I suspect you'll want to change that.
Mixing lock with asynchronous code can be problematic. You should be absolutely sure that this is what you really want to do.
It may be worthwhile stepping back and reconsidering what you are actually wanting to do with this code.

Return a Task from a method with type void

I would like to create a task to run serial commands on. At this time I do not need to return anything from the method that is doing the work. This will probably change later, but I am now curious as to how this.
This is what I have. I would like to use a separate method for the task instead of creating an anonymous action. I have tried returning void, with the result of "void can not be explicitly converted to a Task". I have also tried. Task<void>. The Last thing I have tried is returning a Task, but I receive, error "Not all Code paths return a value" and "Can not implicily convert void to type task"
In the pass I have used a Thread to accomplish this, but I'd like to use Tasks this time around.
internal class Hardware
{
private EventHandler<SequenceDoneEventArgs> SequenceDone;
private List<Step> Steps;
private System.IO.Ports.SerialPort comport = null;
private Task SequenceTask;
private CancellationTokenSource RequestStopSource;
private CancellationToken RequestStopToken;
private void Initialize()
{
comport = new System.IO.Ports.SerialPort("COM2", 115200, System.IO.Ports.Parity.None,8);
comport.DataReceived += Comport_DataReceived;
}
public async void RunSequence()
{
if (comport == null)
{
Initialize();
}
if (!comport.IsOpen)
{
comport.Open();
}
RequestStopSource = new CancellationTokenSource();
RequestStopToken = RequestStopSource.Token;
SequenceTask = await Task.Run(() => { doSequence(); });
}
private Task doSequence()
{
//** Run Sequence stuff here
}
}
ETA:
In the end this is my the complete solution
internal class Hardware
{
private EventHandler<SequenceDoneEventArgs> SequenceDone;
private List<Step> Steps;
private System.IO.Ports.SerialPort comport = null;
private Task SequenceTask;
private CancellationTokenSource RequestStopSource;
private CancellationToken RequestStopToken;
private void Initialize()
{
comport = new System.IO.Ports.SerialPort("COM2", 115200, System.IO.Ports.Parity.None,8);
comport.DataReceived += Comport_DataReceived;
}
public async void RunSequence()
{
if (comport == null)
{
Initialize();
}
if (!comport.IsOpen)
{
comport.Open();
}
RequestStopSource = new CancellationTokenSource();
RequestStopToken = RequestStopSource.Token;
SequenceTask = await Task.Factory.StartNew(async () => { await doSequence(); });
}
private Task doSequence()
{
//** Run Sequence stuff here
//return null;
return Task.CompletedTask;
}
}
Just mark doSequence as async (assuming it uses await):
private async Task doSequence()
Also, it's a good idea to return this Task in the delegate you pass to Task.Run:
SequenceTask = await Task.Run(() => doSequence());
I would like to create a task to run serial commands on.
This leads me to believe that using async and Task may not be the best solution for your scenario. I suggest you look into TPL Dataflow.
SequenceTask = await Task.Factory.StartNew(async() => { await doSequence(); });
Also your RunSequence() should return Task instead of void.
Actually if you await the Task this should result in the same:
SequenceTask = await doSequence();

Win8 C# trouble with async/await

Why in the second case the exception is thrown?
async void f() {
await media.InitializeAsync();
isInit = true;
capturePreview.Source = media;
await media.StartPreviewAsync(); // Ok
}
// ------------------------------------------
void f() {
StaticClass.g(capturePreview.Source);
}
public static class StaticClass {
public static async void g(MediaSource source) {
await media.InitializeAsync();
isInit = true;
source = media;
await media.StartPreviewAsync(); // Unknown Exception (It's seems that media isn't init)
}
}
The f() function use a function which include async.
So I think the f() should be signed async as well.
Like this: void async f(){...}
If you really want media to be initialized, why don't you do the rest of the code in the .done function ?
void f() {
StaticClass.g(capturePreview.Source);
}
public static class StaticClass {
public static async void g(MediaSource source) {
media.InitializeAsync().done(
isInit = true;
source = media;
await media.StartPreviewAsync(););
}
}
Even if I'm not fully sure you can do an async within a done callback.

Categories

Resources