I'm using Reactive Extensions in combination with async/await to simplify my socket protocol implementation. There's some actions that have to be performed when specific message arrives (e.g. send 'pong' to each 'ping' -message) and there's also some method's where we have to wait for some specific responses asynchronously. The following example illustrates this:
private Subject<string> MessageReceived = new Subject<string>();
//this method gets called every time a message is received from socket
internal void OnReceiveMessage(string message)
{
MessageReceived.OnNext(message);
ProcessMessage(message);
}
public async Task<string> TestMethod()
{
var expectedMessage = MessageReceived.Where(x => x.EndsWith("D") && x.EndsWith("F")).FirstOrDefaultAsync();
await SendMessage("ABC");
//some code...
//if response we are waiting for comes before next row, we miss it
return await expectedMessage;
}
TestMethod() sends "ABC" to the socket and continues when for example "DEF" is received (there might be some other messages before that).
This works almost, but there's a race condition. It seems that this code won't listen for messages until return await expectedMessage; And this is a problem, since sometimes the message arrives before that.
FirstOrDefaultAsync won't work here nicely: It doesn't subscribe until the await line, which leaves you with a race condition (as you point out). Here's how you can replace it:
var expectedMessage = MessageReceived
.Where(x => x.EndsWith("D") && x.EndsWith("F"))
.Take(1)
.Replay(1)
.RefCount();
using (var dummySubscription = expectedMessage.Subscribe(i => {}))
{
await SendMessage("ABC");
//Some code... goes here.
return await expectedMessage;
}
.Replay(1) makes sure that a new subscription gets the most recent entry, assuming one exists. It only works though if there's a subscriber listening, hence dummySubscription.
Related
When using IObservable.LastAsync() to force my console app to wait on the result of an API call using Flurl, that API call is never made and the main thread deadlocks and never returns from LastAsync(). My goals are:
Since this is a console app, I can't really "subscribe" to the API call since that would allow the main thread to continue, likely causing it to exit prior to the API call completing. So I need to block until the value is obtained.
The API call should be deferred until the first subscriber requests a value.
Second and onward subscribers should not cause another API call, instead the last value from the stream should be returned (this is the goal of using Replay(1))
Here is an example that reproduces the issue:
public static class Program
{
public static async Task Main(string[] args)
{
var obs = Observable.Defer(() =>
"https://api.publicapis.org"
.AppendPathSegment("entries")
.GetJsonAsync()
.ToObservable())
.Select(x => x.title)
.Replay(1);
var title = await obs.LastAsync();
Console.WriteLine($"Title 1: {title}");
}
}
How can I modify my example to ensure that all 3 requirements above are met? Why does my example cause a deadlock?
Replay returns "connectable" observable, and you need to call Connect() method on it to start it going. Without that call, it does not subscribe to the underlying observable and does not emit items to its own subscribers, so that's why you have a "deadlock".
In this case instead of manually connecting, you can use RefCount() extension method which will automatically connect it on first subscriber and disconnect on when last subscriber unsubscribes. So:
public static async Task Main(string[] args) {
var obs = Observable.Defer(() =>
"https://api.publicapis.org"
.AppendPathSegment("entries")
.GetJsonAsync()
.ToObservable())
.Select(x => x.count)
.Replay(1)
.RefCount();
// makes request
var title = await obs.LastAsync();
Console.WriteLine($"Title 1: {title}");
// does not make request, obtains from replay cache
title = await obs.LastAsync();
Console.WriteLine($"Title 2: {title}");
}
You can also use AutoConnect method:
.Replay(1)
.AutoConnect(1);
This will automatically connect on first subscriber but will never disconnect (in your case shouldn't matter).
Problem: I have a subscription to a never ending messaging service, my code needs to check if any message satisfies the condition, if it is satisfied, then close the subscription before all the messages are processed and return true. If I have processed all the messages and the condition isn't satisfied then I need to close the subscription and return false.
For example, the condition is foo = 5:
message dataset early success :
msg1: foo=1
msg2: foo=2
msg3: foo=5 <= condition satisfied, return true and stop processing
msg4: foo=6
message dataset failure :
msg1: foo=1
msg2: foo=2
msg3: foo=3
msg4: foo=4 <= no more messages, return false and stop processing
The subscription I use has a synchronous method that I have to pass an async EventHandler.
Here is my functioning code that works for both scenarios, lastMessageReceivedDateTime tracks when a message was last received (to identify the end of the messages) and _conditionStatisfied tells me if I've got my data:
private DateTime lastMessageReceivedDateTime;
private bool _conditionSatisfied;
public Task<bool> CheckSubscription(IThirdParyCode connection)
{
var subscription = connection.Subscribe(async (obj, args) =>
{
lastMessageReceivedDateTime = DateTime.Now;
if(args.Message.foo == 5)
{
_conditionSatisfied = true;
}
});
while (lastMessageReceivedDateTime.AddSeconds(1) > DateTime.Now && !_conditionSatisfied)
{
Thread.Sleep(500);
}
subscription?.Unsubscribe();
return _activityCheckSatisfied;
}
This works, but I wanted to know if there was a better solution.
Note: I can't simply await the async method, as it never returns/completes until I unsubscribe.
More info: The type of the connection is an IStanConnection (from NATS), and the signature of Subscribe is:
IStanSubscription Subscribe(string subject, StanSubscriptionOptions options,
EventHandler<StanMsgHandlerArgs> handler);
I had simplified the signature to focus on the code I had issue with.
Based on your code example I can assume that the message stream ends if there were no new messages within a second of the last message.
Your solution can be modified to eliminate active waiting loop and replace it with single await call. It would be based on two tasks:
First task would track successful completion (_conditionSatisfied in your example) and is going to be set by TaskCompletionSource.SetResult
Second task would try to signal end of the stream by using combination of CancellationToken task wrapper (example implementation of such wrapper) and CancellationTokenSource.CancelAfter which would try to cancel task after each iteration with deferral. This should replace lastMessageReceivedDateTime.AddSeconds(1) > DateTime.Now condition.
Modified code should look like this:
private CancellationTokenSource streamEndCancellation = new CancellationTokenSource();
private TaskCompletionSource<bool> satisfiedCompletionSource = new TaskCompletionSource<bool>();
public async Task<bool> CheckSubscription(IThirdParyCode connection)
{
// CancellationTokenTaskSource is in third-party library and not part of .NET
var streamEndSource = new CancellationTokenTaskSource<bool>(streamEndCancellation.Token);
var subscription = connection.Subscribe(async (obj, args) =>
{
lastMessageReceivedDateTime = DateTime.Now;
if(args.Message.foo == 5)
{
satisfiedCompletionSource.SetResult(true);
}
streamEndCancellation.CancelAfter(1000);
});
Task<bool> actualTask = await Task.WhenAny<bool>(satisfiedCompletionSource.Task, streamEndSource.Task);
subscription?.Unsubscribe();
return !actualTask.IsCanceled;
}
Sometimes I need to start an async job which works very slow. I don't care if that job success and I need to continue working on my current thread.
Like sometimes I need to send an Email or SMS which works very slow. I need to respond to the web client as soon as possible so I don't want to await it.
I have googled this question and some articles suggest me to write like this:
// This method has to be async
public async Task<Response> SomeHTTPAction()
{
// Some logic...
// ...
// Send an Email but don't care if it successfully sent.
Task.Run(() => _emailService.SendEmailAsync());
return MyRespond();
}
Or like this:
// This method has to be async
public async Task<Response> SomeHTTPAction()
{
// Some logic...
// ...
// Send an Email but don't care if it successfully sent.
Task.Factory.StartNew(() => _emailService.SendEmailAsync());
return MyRespond();
}
There will be a warning says: before the call is completed. Consider applying the 'await' operator to the result of the call.
So what if I really awaited it? What is the best practice in C# to 'fire and forget', just call an async method without waiting for its completion?
A standalone discard is the best way to avoid this warning.
_ = Task.Run(() => _emailService.SendEmailAsync());
Discards are dummy variables and can be used to ignore the Task object returned by an asynchronous operation.
https://learn.microsoft.com/en-us/dotnet/csharp/discards#a-standalone-discard
If you truly just want to fire and forget. Simply don't call use await.
// It is a good idea to add CancellationTokens
var asyncProcedure = SomeHTTPAction(cancellationToken).ConfigureAwait(false);
// Or If not simply do:
var asyncProcedure = SomeHTTPAction().ConfigureAwait(false);
If you want to use the result output later its gets trickier. But if it is truly fire and forget the above should work
A Cancellation token allows interrupts and canceling procedures. If you are using Cancellation token you will need to use it everywhere from the retrieval straight through to the calling method (Turtles all the way down).
I used ConfigureAwait(false) to prevent deadlocks. Here for more information
EDIT
See the second answer that uses 'Task.Factory.StartNew' I gave this answer some time ago. At the time I didn't realise that the way I did it at the time doesn't ensure completion.
If you need to use async in your function you can also use a discard variable and don't use await. This is also usefull if you have multiple async function calls but you don't need to wait for all of them.
public async function(){
var tmp = await asyncfunction();
...
_ = _httpClient.PutAsync(url, content);
...
}
As Amadan told in the comment that, you need to remove async from your function. then it will stop giving you the warning.
// This method has to be async
public Response SomeHTTPAction()
{
// Some logic...
// ...
// Send an Email but don't care if it successfully sent.
Task.Factory.StartNew(() => _emailService.SendEmailAsync());
return MyRespond();
}
and Task.Factory.StartNew(() => _emailService.SendEmailAsync()); will indeed work on a new thread.
It all depends on what your Async method accepts. Normally it will accept a "special" class that also holds an event. You can subscribe your callback method to that event and pass it along with the method. When it's finished, your callback method will be called.
An example of this (for sockets) would be:
public void CreateSocket()
{
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
SocketAsyncEventArgs sockAsync = new SocketAsyncEventArgs();
sockAsync.Completed += SockAsync_Completed;
s.ConnectAsync(sockAsync);
}
private void SockAsync_Completed(object sender, SocketAsyncEventArgs e)
{
//Do stuff with your callback object.
}
It all depends on what the method you are trying to call can accept. I would look at the documentation for more help on that specifically.
I am curious why this hasn't been suggested.
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
//what ever code here...e.g.
DoSomething();
UpdateSomething();
}).Start();
It just fires off a separate thread.
I am trying to invoke a method from another .dll file .
It is sending a message through the VPN then Return the RecievedMessage from another computer.
As you now it takes time to sending and receiving message and VpnObject just send message and I should wait for listener to invoke the RecievedMessage.
This method is like this!
public string RecievedMessage()
{
string Recieved ;
// Some VPN Code and then return the result;
return Recieved;
}
public string SendAndRecieveMessage(string MessageToSend)
{
string RecievedAnswer = string.Empty;
// Now Sending Message through the VPN
VpnObject.SendMessage(MessageToSend);
//Then want to Recieve the answer and return the answer here .
return RecievedAnswer;
}
I'm just thinking how can wait for RecievedMessage to invoke then return the result .
You know it is simple to use a variable and assign it value and check for while but it reduced the performance dramatically .
Is there anyway to continue from SendAndRecieveMessage just when RecievedMessage invoked ? (I think it is something with async and await but don't know how!)
Edit :VpnObject is just a sender and receiver through the vpn . it contains a simple socket send and a listener that invoke a method(RecievedMessage) when new message received .
Whether or not you have an alternative to polling depends on whether the library you are using provides any events or callbacks that will tell you when the request has completed.
Either way, the standard approach to exposing the deferred result of an asynchronous operation is to use a Task. Your method signature would look like this:
public Task<string> SendAndRecieveMessage(string MessageToSend)
Now, how you actually implement the method depends on what API VpnObject exposes. TaskCompletionSource is very useful for this kind of thing.
If VpnObject has an event that fires when the request completes:
public Task<string> SendAndReceiveMessage(string messageToSend)
{
var tcs = new TaskCompletionSource<string>();
...
VpnObject.OnMessageReceived += (s, e) => tcs.SetResult(e.Message);
...
return tcs.Task;
}
If VpnObject can accept a callback that it will invoke when the request completes:
public Task<string> SendAndReceiveMessage(string messageToSend)
{
var tcs = new TaskCompletionSource<string>();
...
VpnObject.OnMessageReceived(message => tcs.SetResult(message));
...
return tcs.Task;
}
If VpnObject doesn't support any of this, you can fall back to polling:
public async Task<string> SendAndReceiveMessage(string messageToSend)
{
var tcs = new TaskCompletionSource<string>();
...
while(!VpnObject.IsMessageReceived)
await Task.Delay(500); // Adjust to a reasonable polling interval
...
return VpnObject.Message;
}
You know it is simple to use a variable and assign it value and check for while but it reduced the performance dramatically .
A spin while loop is definitely not the way to implement this. Even with a sleep, it's clunky, and C# has multiple ways to solve this problem.
It's not entirely clear how your VPN Send and Receive method works, but the idea for solving this is to either use a callback approach, or as you noted, use C# async framework.
Without more details on the VPN Object, I'll just have to have some stub methods. The idea is to create a Task that returns the string, mark it as an async task, then await for it to complete. In your case, the task is receiving the VPN response string.
Something like this.
public Task<string> ReceivedMessage()
{
//get the response from the VPN Object.
string Received = VpnObject.GetResponse();
var ts = new TaskCompletionSource<string>();
ts.SetResult(Received);
// Some VPN Code and then return the result;
return ts.Task;
}
public async Task<string> SendAndReceiveMessageAsync(string MessageToSend)
{
string result = string.Empty;
// Now Sending Message through the VPN
VpnObject.SendMessage(MessageToSend);
result = await ReceivedMessage();
return result;
}
I want to create a Bot class in C# for slack to let services create and consume messages for our company. To make it easy for our services to use, I have it just called with Connect() and use an event to let the caller know when there's a message. This is basically how it will be called:
SlackBot bot = new SlackBot(TOKEN);
bot.OnReceiveMessage += message => {
Console.WriteLine("DELEGATE GOT MESSAGE: '{0}'", message);
};
bot.Connect();
The Connect() method calls an internal Receive() method that calls itself after every message:
public delegate void MessageReceivedDelegate(string message);
public event MessageReceivedDelegate OnReceiveMessage;
void Receive()
{
_ReceiveTask = _Client.ReceiveAsync(_ClientBuffer, _CancellationToken);
_ReceiveTask.ContinueWith(twsrr =>
{
WebSocketReceiveResult result = twsrr.Result;
string message = Encoding.ASCII.GetString(_ClientBuffer.Array,
_ClientBuffer.Offset, result.Count);
OnReceiveMessage(message);
Receive();
});
}
So the largest buffer acceptable is 64k, do I need to check result.EndOfMessage and use a MemoryStream or something to to keep adding bytes to until I get the end of the message, then send it?
Looking at the RFC, that seems to be the case to me. I'm less familiar with the WebSocket protocol than the underlying TCP and other network protocols, but if on each call to ReceiveAsync() you actually received a complete message, there would not seem to be a need for the EndOfMessage property on the result.
Note also that your code could benefit from being written in the async/await pattern:
async Task Receive()
{
WebSocketReceiveResult result = await _Client.ReceiveAsync(_ClientBuffer, _CancellationToken);
if (result.Count != 0 || result.CloseStatus == WebSocketCloseStatus.Empty)
{
string message = Encoding.ASCII.GetString(_ClientBuffer.Array,
_ClientBuffer.Offset, result.Count);
OnReceiveMessage(message);
await Receive();
}
}
Or, if you prefer, change the Receive() to async but leave it as void and don't await it. It's an exception to the general rule for async methods, but it would avoid the I/O building a chain of continuations that only gets resolved when the connection is actually closed.