cancel GetAsync request from outside of method - c#

I have large numbers of async requests. At some point, when application is deactivated (paused) I need cancel all requests. I'm looking for a solution to cancel requests outside of async method. can someone point me in a right direction?
Here is chunks of code.
The async method:
public async void GetDetailsAsync(string url)
{
if (this.LastDate == null)
{
this.IsDetailsLoaded = "visible";
NotifyPropertyChanged("IsDetailsLoaded");
Uri uri = new Uri(url);
HttpClient client = new HttpClient();
HtmlDocument htmlDocument = new HtmlDocument();
HtmlNode htmlNode = new HtmlNode(0, htmlDocument, 1);
MovieData Item = new MovieData();
string HtmlResult;
try
{
HtmlRequest = await client.GetAsync(uri);
HtmlResult = await HtmlRequest.Content.ReadAsStringAsync();
}
...
calling method:
for (int i = 0; i < App.ViewModel.Today.Items.Count; i++)
{
App.ViewModel.Today.Items[i].GetDetailsAsync(App.ViewModel.Today.Items[i].DetailsUrl);
}
deactivate event:
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
//Here i need to stop all requests.
}

You just create a single (shared) instance of CancellationTokenSource:
private CancellationTokenSource _cts = new CancellationTokenSource();
Then, tie all asynchronous operations into that token:
public async void GetDetailsAsync(string url)
{
...
HtmlRequest = await client.GetAsync(uri, _cts.Token);
...
}
Finally, cancel the CTS at the appropriate time:
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
_cts.Cancel();
_cts = new CancellationTokenSource();
}

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;
}

UI hangs when multiple httpclient or httpresponse sent

trying to get page source from list of urls. but it hangs the UI. i would prefer httpclient so i can use single instance. please let me know what can i do so it wont hang the ui during the process.
async Task AccessTheWebAsync(CancellationToken ct)
{
HttpClient client = new HttpClient();
List<string> urlToSearch = UrlToSearchList();
IEnumerable<Task<string>> downloadTasksQuery =from url in urlToSearch select ProcessURL2(url, client, ct);
List<Task<string>> downloadTasks = downloadTasksQuery.ToList();
while (downloadTasks.Count > 0)
{
Task<string> firstFinishedTask = await Task.WhenAny(downloadTasks);
downloadTasks.Remove(firstFinishedTask);
string length = await firstFinishedTask;
resultsTextBox.Text += $"\r\nLength of the download: {length}";
}
}
private List<string> UrlToSearchList()
{
var urlToSearchList = new List<string>();
foreach (var item in lbURLToSearch.Items)
{
urlToSearchList.Add(item.ToString());
}
return urlToSearchList;
}
async Task<string> ProcessURL2(string url, HttpClient client, CancellationToken ct)
{
HttpResponseMessage response = await client.GetAsync(url, ct);
var contents = await response.Content.ReadAsStringAsync();
return contents;
}
private void CancelButton_Click(object sender, EventArgs e)
{
if (cts != null)
{
cts.Cancel();
}
}
private async void StartButton_Click(object sender, EventArgs e)
{
resultsTextBox.Clear();
cts = new CancellationTokenSource();
try
{
await AccessTheWebAsync(cts.Token);
resultsTextBox.Text += "\r\nDownloads complete.";
}
catch (OperationCanceledException)
{
resultsTextBox.Text += "\r\nDownloads canceled.\r\n";
}
catch (Exception)
{
resultsTextBox.Text += "\r\nDownloads failed.\r\n";
}
cts = null;
}
i also tried various async methods. still same result, it hangs. below is one of the async method i tried.
private async void TsbtnStartURLScraper_Click(object sender, EventArgs e)
{
for (int i = 0; i < lbURLToSearch.Items.Count; i++)
{
txtScrapeURL.Text = lbURLToSearch.Items[i].ToString();
string urlAddress = lbURLToSearch.Items[i].ToString();
var returnedresult = await DownloadStringV1(urlAddress);
resultsTextBox.Text += returnedresult;
}
}
public async Task<String> DownloadStringV1(String url)
{
// good code
var request = await reusableHTTPClient.GetAsync(url);
var download = await request.Content.ReadAsStringAsync();
return download;
}
NOTE: what i am trying to do is, loop through a list of url and get page source from them. but when i am doing this it is freezing the form.

C# uwp client server show components

I have a problem with my server uwp side.
I try do unhide some components like this but my app hangs on :
private async void StreamSocketListener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
string request;
using(var streamReader = new StreamReader(args.Socket.InputStream.AsStreamForRead()))
{
request = await streamReader.ReadLineAsync();
}
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => this.txtBlk_Events.Text = request);
if (request.Length > 0)
{
btnSend.Visibility = Visibility.Visible;
}
}
It is possible to do that or not ?
Thanks for your answer
Visibility is a UI property and must be set on the UI thread. In your case it will run on the same thread the ConnectionReceived event handler is run on (thanks to async/await) which is likely not the UI thread. You should instead set the Visibility within the Dispatcher.RunAsync call to make sure it runs on UI thread.
private async void StreamSocketListener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
string request;
using(var streamReader = new StreamReader(args.Socket.InputStream.AsStreamForRead()))
{
request = await streamReader.ReadLineAsync();
}
var setVisibility = request.Length > 0;
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
this.txtBlk_Events.Text = request;
if ( setVisibility )
{
btnSend.Visibility = Visibility.Visible;
}
});
}

Run method Safe cancellation?

I want to call a function in a different thread than my GUI.
I used the below code to trigger the function:
private void button1_Click(object sender, EventArgs e)
{
tokensource = new CancellationTokenSource();
var token = tokensource.Token;
Task.Run(()=>foo() , token);
}
private void foo()
{
// Uses some resources
}
private void button2_Click(object sender, EventArgs e)
{
tokenSource.Cancel();
}
How can I safely close the occupied resources in foo() when the task is cancelled?
You need to pass the token to the function as well. The cancellation token passed to Task.Run won't abort an already running task, it will prevent a scheduled task from running.
Inside foo, you can check the token for cancellation and return, or throw an exception. You can use a using block to dispose resources safely. Eg:
private void foo(CancellationToken token)
{
using(var reader=new StreamReader(somePath)
{
string line;
// Read the line if no cancellation was requested
while (!token.IsCancellationRequested && (line = sr.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
}
This code reads a line only if cancellation wasn't requested and returns quietly otherwise
You can also throw an OperationCancelledException by calling CancellationToken.ThrowIfCancellationRequested
private void foo(CancellationToken token)
{
using(var reader=new StreamReader(somePath)
{
string line;
// Read the line if no cancellation was requested
while ((line = sr.ReadLine()) != null)
{
token.ThrowIfCancellationRequested();
Console.WriteLine(line);
}
}
}
This will throw an exception that will be raised in the calling code when the task's result is retrieved, eg when using await Task.Run(..) or Task.Run(..).Wait()
Your Method should handle the CancellationToken like that:
public static void Main(string[] args)
{
var tokenSource = new CancellationTokenSource();
Console.WriteLine("Press CTRL+C to cancel important work");
Console.CancelKeyPress += (sender, eventArgs) => {
eventArgs.Cancel = true;
tokenSource.Cancel();
};
var task = Task.Run(() => foo(tokenSource.Token));
task.Wait();
WaitFor(action: "exit");
}
private static void foo(CancellationToken token)
{
const int Times = 10;
for (var x = 0; x < Times && token.IsCancellationRequested == false; ++x) {
Console.WriteLine("Important work");
Task
.Delay(200)
.Wait();
}
Console.WriteLine($"Free resources: {token.IsCancellationRequested}");
}
public static void WaitFor(ConsoleKey consoleKey = ConsoleKey.Escape, string action = "continue")
{
Console.Write($"Press {consoleKey} to {action} ...");
var consoleKeyInfo = default(ConsoleKeyInfo);
do {
consoleKeyInfo = Console.ReadKey(true);
}
while (Equals(consoleKeyInfo.Key, consoleKey) == false);
Console.WriteLine();
}
BR
incureforce
The code you're spinning up in a task should be responsible of taking into account cancellation. And the cancellation token you pass into the "Task.Run" method will be used only to cancel non-started tasks.

Waiting for async operation to complete

I have a question about how asynchronous methods work on c#. I am trying to get the convertAddressToCoordinate method to set the myLocation variable to a GeoCoordinate. However, the compareDistance method is called even before the myLocation value is set. How can I ensure that the myLocation value is not null before I call compareDistance()?
public GeoCoordinate myLocation = null;
public void returnClosestCurrent(string address)
{
convertAddressToCoordinate(address);
compareDistance(myLocation);
}
public void convertAddressToCoordinate(string add)
{
WebClient wc = new WebClient();
wc.DownloadStringCompleted += wc_DownloadStringCompleted;
wc.DownloadStringAsync(new Uri("http://maps.googleapis.com/maps/api/geocode/json?address=1600+bay+st&sensor=false"));
}
void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
XDocument xdoc = XDocument.Parse(e.Result, LoadOptions.None);
var data = from query in xdoc.Descendants("location")
select new Location
{
lat = (string)query.Element("lat"),
lng = (string)query.Element("lng")
};
GeoCoordinate destinationGeo = new GeoCoordinate(Convert.ToDouble(data.ElementAt(0).lat), Convert.ToDouble(data.ElementAt(0).lng));
myLocation = destinationGeo;
}
you can use the await keyword for getting values of async task
http://msdn.microsoft.com/en-us/library/vstudio/hh156528.aspx
Looking at your code, seems to me that the compareDistance(myLocation); should be called only in the end of the wc_DownloadStringCompleted method.
After that change, everything should work fine.
You would want to await the wc.DownloadStringAsync
public async Task returnClosestCurrent(string address)
{
await convertAddressToCoordinate(address)
.ContinueWith(t => compareDistance(myLocation));
}
public async Task convertAddressToCoordinate(string add)
{
WebClient wc = new WebClient();
wc.DownloadStringCompleted += wc_DownloadStringCompleted;
await wc.DownloadStringAsync(new Uri("http://maps.googleapis.com/maps/api/geocode/json?address=1600+bay+st&sensor=false"));
}
The pattern with async/await is the code tends to turn everything into async/await
It could be refactored to use WebClient.DownloadStringTaskAsync
public async Task ReturnClosestCurrent(string address)
{
await convertAddressToCoordinate(address)
.ContinueWith(t => compareDistance(t.Result));
}
public async Task<GeoCoordinate> ConvertAddressToCoordinate(string add)
{
WebClient wc = new WebClient();
wc.DownloadStringCompleted += wc_DownloadStringCompleted;
var content = await wc.DownloadStringTaskAsync(new Uri("http://maps.googleapis.com/maps/api/geocode/json?address=1600+bay+st&sensor=false"));
return ParseContent(content);
}
private GeoCoordinate ParseContent(string content)
{
XDocument xdoc = XDocument.Parse(content, LoadOptions.None);
var data = from query in xdoc.Descendants("location")
select new Location
{
lat = (string)query.Element("lat"),
lng = (string)query.Element("lng")
};
GeoCoordinate destinationGeo = new GeoCoordinate(Convert.ToDouble(data.ElementAt(0).lat), Convert.ToDouble(data.ElementAt(0).lng));
return destinationGeo;
}

Categories