c# wrapping a Task into a another Task - c#

I am trying to use inter process communication to send from one instance of my program its content to the other instance. The code I have under here is working but it makes the first instance freeze and only continues running when the second instansce is created and the data is passed back.I suspect its because messaging_server0() is not a async task. How would one approach this problem. Is there a way to make messaging_server0 async? or am I missing something?
main loop contains this piece of code
var makeTask = Task<string>.Factory.StartNew(() => pipe_server.messaging_server0());
if (makeTask.Result != null) {
dataGrid_logic.DataGridLoadTarget(makeTask.Result);
}
and on the other side I have messaging_server0
public static string messaging_server0()
{
using (var messagebus1 = new TinyMessageBus("ExampleChannel"))
{
string ret = null;
messagebus1.MessageReceived += (sender, e) => ret = Encoding.UTF8.GetString(e.Message);
while (true)
{
if (ret != null)
{
return ret;
}
}
}
}
The method names are going to be refactored.

I would suggest following approach:
public static async Task<string> messaging_server0()
{
using (var messagebus1 = new TinyMessageBus("ExampleChannel"))
{
var taskCompletition = new TaskCompletionSource<string>();
messagebus1.MessageReceived +=
(sender, e) =>
{
var ret = Encoding.UTF8.GetString(e.Message);
taskCompletition.TrySetResult(ret);
};
return await taskCompletition.Task;
}
}
Obviously you would need to add some error handling, time outs if needed etc.

with the help of alek kowalczyk and some digging I came up with this code
private async void xxx()
{
var makeTask = Task<string>.Factory.StartNew(() => pipe_server.messaging_server());
await pipe_server.messaging_server();
{
dataGrid_logic.DataGridLoadTarget(makeTask.Result);
}
}
plus the snippet he posted.
This works perfectly. thanks

Related

Why first click event of button not working

I have app(net4.7.2) like this:
Program is simple, when user presses OK, im sending request to steam market to get informations about item which user entered (item steam market url) to textbox.
But when im trying to send request, first click event of button not working:
private void btnOK_Click(object sender, EventArgs e)
{
if (txtItemURL.Text.StartsWith("https://steamcommunity.com/market/listings/730/") == true)
{
Helpers.Helper.BuildURL(txtItemURL.Text);
SteamMarketItem SMI = Helpers.Helper.GetItemDetails();
lblPrice.Text = SMI.LowestPrice.ToString() + "$";
pbItemImage.ImageLocation = SMI.ImagePath;
Helpers.Helper.Kontrollar_BerpaEt();
}
else
{
Helpers.Helper.Kontrollar_SifirlaYanlisDaxilEdilib();
}
}
Method GetItemDetails():
public static SteamMarketItem GetItemDetails()
{
WinForms.Control.CheckForIllegalCrossThreadCalls = false;
Task.Run(() =>
{
try
{
using (HttpClient client = new HttpClient())
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
/* Get item info: */
var ResultFromEndpoint1 = client.GetAsync(ReadyEndpointURL1).Result;
var Json1 = ResultFromEndpoint1.Content.ReadAsStringAsync().Result;
dynamic item = serializer.Deserialize<object>(Json1);
marketItem.LowestPrice = float.Parse(((string)item["lowest_price"]).Replace("$", "").Replace(".", ","));
/* Get item image: */
var ResultFromEndpoint2 = client.GetAsync(ReadyEndPointURL2).Result;
var Json2 = ResultFromEndpoint2.Content.ReadAsStringAsync().Result;
var html = ((dynamic)serializer.Deserialize<object>(Json2))["results_html"];
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(html);
marketItem.ImagePath = htmlDoc.DocumentNode.SelectSingleNode("//img[#class='market_listing_item_img']").Attributes["src"].Value + ".png";
Kontrollar_BerpaEt();
}
}
catch
{
Kontrollar_SifirlaYanlisDaxilEdilib();
}
});
return marketItem;
}
Class SteamMarketItem:
public class SteamMarketItem
{
public string ImagePath { get; set; }
public float LowestPrice { get; set; }
}
When im using Task.Run() first click not working, without Task.Run() working + but main UI thread stopping when request not finished.
I have no idea why this happens, I cant find problem fix myself, I will be glad to get help from you. Thanks.
If you want to use async you need to change your event handler to async so you can use await, please see the following:
1. Change your Event handler to async void, async void is acceptable on event handler methods, you should try to use async Task in place of async void in most other cases, so change your method signature to the following:
private async void btnOK_Click(object sender, EventArgs e)
{
if (txtItemURL.Text.StartsWith("https://steamcommunity.com/market/listings/730/") == true)
{
Helpers.Helper.BuildURL(txtItemURL.Text);
//here we use await to await the task
SteamMarketItem SMI = await Helpers.Helper.GetItemDetails();
lblPrice.Text = SMI.LowestPrice.ToString() + "$";
pbItemImage.ImageLocation = SMI.ImagePath;
Helpers.Helper.Kontrollar_BerpaEt();
}
else
{
Helpers.Helper.Kontrollar_SifirlaYanlisDaxilEdilib();
}
}
2. You shouldn't need to use Task.Run, HttpClient exposes async methods and you can make the method async, also, calling .Result to block on an async method is typically not a good idea and you should make the enclosing method async so you can utilize await:
//Change signature to async and return a Task<T>
public async static Task<SteamMarketItem> GetItemDetails()
{
WinForms.Control.CheckForIllegalCrossThreadCalls = false;
//what is marketItem?? Where is it declared?
try
{
using (HttpClient client = new HttpClient())
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
/* Get item info: */
var ResultFromEndpoint1 = await client.GetAsync(ReadyEndpointURL1);
var Json1 = await ResultFromEndpoint1.Content.ReadAsStringAsync();
dynamic item = serializer.Deserialize<object>(Json1);
marketItem.LowestPrice = float.Parse(((string)item["lowest_price"]).Replace("$", "").Replace(".", ","));
/* Get item image: */
var ResultFromEndpoint2 = await client.GetAsync(ReadyEndPointURL2);
var Json2 = await ResultFromEndpoint2.Content.ReadAsStringAsync();
var html = ((dynamic)serializer.Deserialize<object>(Json2))["results_html"];
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(html);
marketItem.ImagePath = htmlDoc.DocumentNode.SelectSingleNode("//img[#class='market_listing_item_img']").Attributes["src"].Value + ".png";
Kontrollar_BerpaEt();
}
}
catch
{
Kontrollar_SifirlaYanlisDaxilEdilib();
}
//what is marketItem?? Where is it declared?
return marketItem;
}

Problems working with async Task and Textbox.Text = "Hello"

First of all, sorry because I am so new at C# and I decided to make this question because I have been choked in this for hours.
I have an GUI that works with Google Cloud Speech services and make a Speech-to-Text operation. I share with you the whole method that runs when a button is clicked:
private async Task<object> StreamingMicRecognizeAsync(int seconds)
{
if (NAudio.Wave.WaveIn.DeviceCount < 1)
{
Console.WriteLine("No microphone!");
return -1;
}
GoogleCredential googleCredential;
using (Stream m = new FileStream(#"..\..\credentials.json", FileMode.Open))
googleCredential = GoogleCredential.FromStream(m);
var channel = new Grpc.Core.Channel(SpeechClient.DefaultEndpoint.Host,
googleCredential.ToChannelCredentials());
var speech = SpeechClient.Create(channel);
var streamingCall = speech.StreamingRecognize();
// Write the initial request with the config.
await streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
StreamingConfig = new StreamingRecognitionConfig()
{
Config = new RecognitionConfig()
{
Encoding =
RecognitionConfig.Types.AudioEncoding.Linear16,
SampleRateHertz = 48000,
LanguageCode = "es-ES",
},
InterimResults = true,
}
});
// Read from the microphone and stream to API.
object writeLock = new object();
bool writeMore = true;
var waveIn = new NAudio.Wave.WaveInEvent();
waveIn.DeviceNumber = 0;
waveIn.WaveFormat = new NAudio.Wave.WaveFormat(48000, 1);
waveIn.DataAvailable +=
(object sender, NAudio.Wave.WaveInEventArgs args) =>
{
lock (writeLock)
{
if (!writeMore) return;
streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
AudioContent = Google.Protobuf.ByteString
.CopyFrom(args.Buffer, 0, args.BytesRecorded)
}).Wait();
}
};
// Print responses as they arrive.
Task printResponses = Task.Run(async () =>
{
while (await streamingCall.ResponseStream.MoveNext(default(CancellationToken)))
{
foreach (var result in streamingCall.ResponseStream
.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
Console.WriteLine(alternative.Transcript);
//Textbox1.Text = alternative.Transcript;
}
}
}
});
waveIn.StartRecording();
Console.WriteLine("Speak now.");
Result_Tone.Text = "Speak now:\n\n";
await Task.Delay(TimeSpan.FromSeconds(seconds));
// Stop recording and shut down.
waveIn.StopRecording();
lock (writeLock) writeMore = false;
await streamingCall.WriteCompleteAsync();
await printResponses;
return 0;
}
My problem is that I want to update the content of the Textbox1control but it doesn´t work. It writes perfectly the output into the console with the line Console.WriteLine(alternative.Transcript); but not into my textbox.
If someone could help I would appreciate so much his help.
The problem is that you're using Task.Run, which means your code will be running on a thread-pool thread.
Instead of calling Task.Run(), just move that code into a separate async method:
async Task DisplayResponses(IAsyncEnumerator<StreamingRecognizeResponse> responses)
{
while (await responses.MoveNext(default(CancellationToken)))
{
foreach (var result in responses.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
Textbox1.Text = alternative.Transcript;
}
}
}
}
Then call that method directly (without Task.Run) from code that's already on the UI thread (e.g. an event handler).
The async machinery will make sure that after the await expression, you're back on the UI thread (the same synchronization context). So the assignment to the Text property will occur on the UI thread, and all should be well.
For example:
// This would be registered as the event handler for a button
void HandleButtonClick(object sender, EventArgs e)
{
var stream = client.StreamingRecognize();
// Send the initial config request
await stream.WriteAsync(...);
// Presumably you want to send audio data...
StartSendingAudioData(stream);
await DisplayResponses(stream.ResponseStream);
}
Tasks run on seperate threads, so you must Invoke an action that will be performed on the control's thread
Textbox1.Invoke(new Action(() =>
{
Textbox1.Text= "";
}));
Edit: For WPF, I believe the equivalent is
Textbox1.Dispatcher.Invoke(new Action(() =>
{
Textbox1.Text= "";
}));
have you tried using Dispatcher.InvokeASync()?
await Dispatcher.InvokeAsync(() => {while (await streamingCall.ResponseStream.MoveNext(default(CancellationToken)))
{
foreach (var result in streamingCall.ResponseStream
.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
Textbox1.Text = alternative.Transcript;
}
}
}});

uwp async method skipping code lines

I have an async method with void return type. Here it is
public static async void LoadPlaylists()
{
if(playlistitems.Count==0)
{
var playlists = await ApplicationData.Current.LocalFolder.GetFilesAsync();
var c = playlists.Count;
foreach (var playlist in playlists)
{
var p = await Playlist.LoadAsync(playlist);
var image = new BitmapImage();
if (p.Files.Count == 0)
{
image.UriSource = new Uri("ms-appx:///Assets/Wide310x150Logo.scale-200.png");
}
else
{
image = await Thumbnail(p.Files[0]);
}
playlistitems.Add
(
new PlaylistItem
{
playlist = p,
PlaylistName = playlist.DisplayName,
NumOfVid = p.Files.Count.ToString(),
Thumbnail = image
}
);
}
}
}
It is a public static method so I can use it anywhere, I use it on one page to load some data and it works fine,and it completes this method and then move forward on next code line. as shown below
private void l1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
//some more code
LoadPlaylists();
//some more code
}
But when I use it on another page in another event handler, which is not async method, it just runs the first line, and then it skips whole method and moves forward. I know for sure that it is skipping those lines, bcz i checked with break point, I know it is skipping because it is async but I dnt want that, I just want it to complete the whole method and then move forward. S o that I dont get any problem on next code lines. I am pasting the code below again, to show you with comments what it skipps.
public static async void LoadPlaylists()
{
if(playlistitems.Count==0)
{
//it runs till here, when compiler goes to line below
//it skips whole methods and exits it.
var playlists = await ApplicationData.Current.LocalFolder.GetFilesAsync();
var c = playlists.Count;
foreach (var playlist in playlists)
{
var p = await Playlist.LoadAsync(playlist);
var image = new BitmapImage();
if (p.Files.Count == 0)
{
image.UriSource = new Uri("ms-appx:///Assets/Wide310x150Logo.scale-200.png");
}
else
{
image = await Thumbnail(p.Files[0]);
}
playlistitems.Add
(
new PlaylistItem
{
playlist = p,
PlaylistName = playlist.DisplayName,
NumOfVid = p.Files.Count.ToString(),
Thumbnail = image
}
);
}
}
}
Now below is the code where I am using it in an async event handler, and whre it is causing me problem.
private async void ME_MediaOpened(object sender, RoutedEventArgs e)
{
//When used here, it just skippes everything in the method
//as i described on the comments in code above.
LoadPlaylists();
//after skipping the compiler comes here and tries to execute
//the lines below, which obviously causes exceptions because
above method was never completed to begin with.
var sd = playlistitems.Count;
//some more code
}
You should return the Task instead of void so that you can await for the method to complete.

how to wait for webclient OpenReadAsync to complete

I am using WebClient to download some stuff from internet in Windows Phone 8.1 app.
Below is the sample code i am using in my app - where i am calling below method, but my webclient is not waiting to complete the read operation and returning immediately after OpenReadAsync call.
how can i make sure that my method return operation must wait till OpenReadCompleted event is completed?
I have seen multiple similar questions, but couldn't find a solution.
MyCustomObject externalObj; // my custom object
private static void CheckNetworkFile()
{
try
{
WebClient webClient = new WebClient();
webClient.OpenReadCompleted += (s, e) =>
{
externalObj = myReadWebclientResponse(e.Result); // my custom method to read the response
};
webClient.OpenReadAsync(new Uri("http://externalURl.com/sample.xml", UriKind.Absolute));
}
catch (Exception)
{
externalObj = null;
}
}
I would advise you to use WebClient.OpenReadTaskAsync with a combination of the async/await keywords introduced in .NET 4.5 instead. You need to add the async keyword to your method, make it return a Task and it is advisable to end your method with the Async postfix:
MyCustomObject externalObj;
private static async Task CheckNetworkFileAsync()
{
try
{
WebClient webClient = new WebClient();
Stream stream = await webClient.OpenReadTaskAsync(new Uri("http://externalURl.com/sample.xml", UriKind.Absolute));
externalObj = myReadWebclientResponse(stream);
}
catch (Exception)
{
externalObj = null;
}
}
Edit:
As you said, WebClient.OpenReadTaskAsync isn't available for WP8.1, So lets create an Extension Method so it will be:
public static class WebClientExtensions
{
public static Task<Stream> OpenReadTaskAsync(this WebClient client, Uri uri)
{
var tcs = new TaskCompletionSource<Stream>();
OpenReadCompletedEventHandler openReadEventHandler = null;
openReadEventHandler = (sender, args) =>
{
try
{
tcs.SetResult(args.Result);
}
catch (Exception e)
{
tcs.SetException(e);
}
};
client.OpenReadCompleted += openReadEventHandler;
client.OpenReadAsync(uri);
return tcs.Task;
}
}
Now you can use it on your WebClient.
You can find great reading material in the async-await wiki and by simply filtering by that tag in the search bar.
I hope this is not too off topic, but others who are researching this might like to know that the above code sample can also be used for WCF calls in Silverlight. Be sure to add the Microsoft.Bcl.Async NuGet package first. Here is a WCF code example:
public static async Task<string> AuthenticateAsync(string userName, string password)
{
var dataAccessServiceClient = new DataAccessServiceClient("DataAccessService");
var taskCompletionSource = new TaskCompletionSource<string>();
EventHandler<AuthenticateCompletedEventArgs> completedHandler = null;
completedHandler = (s, args) =>
{
try
{
taskCompletionSource.SetResult(args.Result);
}
catch (Exception e)
{
taskCompletionSource.SetException(e);
}
};
dataAccessServiceClient.AuthenticateCompleted += completedHandler;
dataAccessServiceClient.AuthenticateAsync(userName, password);
return await taskCompletionSource.Task;
}
It can be called like this:
var result = await AuthenticateAsync(username, password);

How can I make many pings asynchronously at the same time?

I just can't seem to understand how to structure an asynchronous call to SendPingAsync. I want to loop through a list of IP addresses and ping them all asynchronously before moving on in the program... right now it takes forever to go through all of them one at a time. I asked a question about it earlier thinking I'd be able to figure out async but apparently I was wrong.
private void button1_Click(object sender, EventArgs e)
{
this.PingLoop();
MessageBox.Show("hi"); //for testing
}
public async void PingLoop()
{
Task<int> longRunningTask = PingAsync();
int result = await longRunningTask;
MessageBox.Show("async call is finished!");
//eventually want to loop here but for now just want to understand how this works
}
private async Task<int> PingAsync()
{
Ping pingSender = new Ping();
string reply = pingSender.SendPingAsync("www.google.com", 2000).ToString();
pingReplies.Add(reply); //what should i be awaiting here??
return 1;
}
I'm afraid I just don't get what is really going on here enough... when should I return a task? When I run this as is I just get a frozen UI and a ping error. I have read the MSDN documentation and tons of questions here and I'm just not getting it.
You'd want to do something like:
private async Task<List<PingReply>> PingAsync()
{
var tasks = theListOfIPs.Select(ip => new Ping().SendPingAsync(ip, 2000));
var results = await Task.WhenAll(tasks);
return results.ToList();
}
This will start off one request per IP in theListOfIPs asynchronously, then asynchronously wait for them all to complete. It will then return the list of replies.
Note that it's almost always better to return the results vs. setting them in a field, as well. The latter can lead to bugs if you go to use the field (pingReplies) before the asynchronous operation completes - by returning, and adding the range to your collection after the call is made with await, you make the code more clear and less bug prone.
What you do here pingSender.SendPingAsync("www.google.com", 2000).ToString(); doesn't make much sense.
Instead you should return pingSender.SendPingAsync("www.google.com", 2000) and
await Task.WhenAll(your all ping requests)
What you want is to start all pings at once:
var pingTargetHosts = ...; //fill this in
var pingTasks = pingTargetHosts.Select(
host => new Ping().SendPingAsync(host, 2000)).ToList();
Now the pings are running. Collect their results:
var pingResults = await Task.WhenAll(pingTasks);
Now the concurrent phase of the processing is done and you can examine and process the results.
Here is how I do it
private delegate void scanTargetDelegate(IPAddress ipaddress);
private Task<PingReply> pingAsync(IPAddress ipaddress)
{
var tcs = new TaskCompletionSource<PingReply>();
try
{
AutoResetEvent are = new AutoResetEvent(false);
Ping ping = new Ping();
ping.PingCompleted += (obj, sender) =>
{
tcs.SetResult(sender.Reply);
};
ping.SendAsync(ipaddress, new object { });
}
catch (Exception)
{
}
return tcs.Task;
}
in a BackgroundWorker I do this
List<Task<PingReply>> pingTasks = new List<Task<PingReply>>();
addStatus("Scanning Network");
foreach (var ip in range)
{
pingTasks.Add(pingAsync(ip));
}
Task.WaitAll(pingTasks.ToArray());
addStatus("Network Scan Complete");
scanTargetDelegate d = null;
IAsyncResult r = null;
foreach (var pingTask in pingTasks)
{
if (pingTask.Result.Status.Equals(IPStatus.Success))
{
d = new scanTargetDelegate(scanTarget); //do something with the ip
r = d.BeginInvoke(pingTask.Result.Address, null, null);
Interlocked.Increment(ref Global.queuedThreads);
}
else
{
if (!ownIPs.Contains(pingTask.Result.Address))
{
failed.Add(pingTask.Result.Address);
}
}
}
if (r != null)
{
WaitHandle[] waits = new WaitHandle[] { r.AsyncWaitHandle };
WaitHandle.WaitAll(waits);
}
public static async Task<bool> PingAsync(string host)
{
try
{
var ping = new System.Net.NetworkInformation.Ping();
var reply = await ping.SendTaskAsync(host);
return (reply.Status == System.Net.NetworkInformation.IPStatus.Success);
}
catch { return false; }
}

Categories