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.
Related
I have a GUI application where data is continously polled by MyMethod and when the Disconnect button is clicked, inside MyMethod the OperationCanceledException
closes the port. Here my_cancelationTokenSource is declared:
public partial class MainWindow : Window
{
private CancellationTokenSource my_cancelationTokenSource;
//...
}
And here MyMethod continously polls data:
private async Task MyMethod(ISerialComms port, byte[] test_telegram)
{
byte[] sent;
my_cancelationTokenSource = new CancellationTokenSource();
try
{
do
{
await Task.Delay(POLL_PERIOD, my_cancelationTokenSource.Token);
if (myFlag == false && sent != null && myPort.IsOpen == true && test_port == false)
{
byte[] received = await telegram.SendReceive(sent, myPort, port, my_cancelationTokenSource.Token);
MyProgressMethod(received, sent);
}
}
while (true);
}
catch (OperationCanceledException)
{
try
{
my_cancelationTokenSource.Dispose();
my_cancelationTokenSource = null;
if (myPort.IsOpen)
{
myPort.Close();
}
}
catch { }
}
}
Here is the disconnect button:
private void Button_Disconnect_Click(object sender, RoutedEventArgs e)
{
my_cancelationTokenSource?.Cancel();
}
And below I have another async method TestParameters() run in series inside another button:
private async void Test_Click(object sender, RoutedEventArgs e)
{
try
{
if (myPort.IsOpen)
{
myFlag = true;
byte[] set_parameter = myObject.CreatePulseParameters((double)NumericUpDown1.Value, (double)NumericUpDown2.Value);
int delay_ms = 20;
await Task.Delay(delay_ms);
await TestParameters(set_parameter);
await Task.Delay(delay_ms * 2);
await TestParameters(set_parameter);
await Task.Delay(delay_ms);
await TestParameters(set_parameter);
await Task.Delay(delay_ms * 4);
await TestParameters(set_parameter);
await Task.Delay(delay_ms);
await TestParameters(set_parameter);
await Task.Delay(delay_ms);
await TestParameters(set_parameter);
await Task.Delay(delay_ms * 3);
await TestParameters(set_parameter);
myFlag = false;
}
}
catch (System.InvalidOperationException)
{
myPort.Close();
}
}
My problem is, when I click Disconnect the port is closed but TestParameters() keeps running. How can this issue be solved?
(One solution was to put if(myPort.IsOpen) before each TestParameters(), but somehow I thought there misght be a more reilable better way)
Wht I need: TestParameters() should stop running as well when the Disconnect button is clicked just like MyMetho().
Edit:
Two other Tasks:
private async Task TestParameters(byte[] set_parameter)
{
if (myPort.IsOpen)
{
int times_try = 5;
int retry = 0;
myFlag = true;
await Task.Delay(500);
do
{
bool validate = await Set_Parameters(set_parameter);
await Task.Delay(500);
if (validate)
break;
if (!validate)
retry++;
}
while (retry < times_try);
}
myFlag = false;
}
And the other one:
private async Task<bool> Set_Parameters(byte[] send_set_parameter_telegram)
{
bool validate = false;
byte[] validate_parameters = pulse.validate_parameters;
if (myPort.IsOpen )
{
await Task.Delay(100);
byte[] receive_error_telegram = await telegram.SendReceive(validate_parameters, myPort, port);
await Task.Delay(100);
byte[] receive_validated_telegram = await telegram.SendReceive(validate_parameters, myPort, port);
}
return validate;
}
This is code from my handler:
public class MoeZdravjeStatusCodeHandler : DelegatingHandler
{
public MoeZdravjeStatusCodeHandler(HttpMessageHandler innerHandler) : base(innerHandler) { }
public IMojLekDataStore<Object> MojLekDataStore => DependencyService.Get<IMojLekDataStore<Object>>();
public bool flag=true;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = new HttpResponseMessage();
response = await base.SendAsync(request, cancellationToken);
int status_code = (int)response.StatusCode;
if (response.IsSuccessStatusCode)
{
return response;
}
else if(status_code == 401 && DependencyService.Get<ISharedFunctions>().GetUserValidation() == true )
{
try
{
if (flag)
{
flag = false;
string date = DateTime.Today.ToString("yyyy-MM-dd") + "MoeZdravje";
string password = Helpers.ServiceHelper.HashPassword(date).ToString();
LoginMoeZdravje log = new LoginMoeZdravje();
log.EZBO = DependencyService.Get<ISharedFunctions>().GetPatient().Ezbo.ToString();
log.PASSWORD = password;
log.CONFIRMED_USER = 1;
var response_token = await MojLekDataStore.GetMoeZdravjeToken(log);
if (response_token != null && !String.IsNullOrEmpty(response_token.Token))
{
flag = true;
DependencyService.Get<ISharedFunctions>().SaveMoeZdravjeToken(response_token.Token);
await Application.Current.MainPage.DisplayAlert("", "ВАШИОТ ТОКЕН Е ОСВЕЖЕН", AppResources.BTNOK);
Application.Current.MainPage = new NavigationPage(new MainMenu());
}
else
{
flag = true;
await Application.Current.MainPage.DisplayAlert("", AppResources.SERVER_ERROR, AppResources.BTNOK);
}
}
else
{
CancellationTokenSource source = new CancellationTokenSource();
cancellationToken = source.Token;
source.Cancel();
source.Dispose();
}
}
catch (AggregateException ae)
{
foreach (Exception e in ae.InnerExceptions)
{
if (e is TaskCanceledException)
Console.WriteLine("Unable to compute mean: {0}",
((TaskCanceledException)e).Message);
else
Console.WriteLine("Exception: " + e.GetType().Name);
}
}
finally
{
}
}
return response;
}
I want when come to await MojLekDataStore.GetToken(log); block every async Task until finish this request because with this request i get a new token from my Api and need to save that token and call the requests to get the data with the new token. I have a tabbedPage with 4 async Tasks and this handler called two times for get a new token but i need one and to start work with the new token.
EDIT
I added CancellationTokenSource source = new CancellationTokenSource();
but i dont know if this could stop other 3 async task ? The flag is used when first 401status_code request come .
private async Task<string> httpClient(CancellationToken cancelToken)
{
HttpClient hc = new HttpClient();
hc.Timeout = new TimeSpan(0, 0, 10);
//Task.Delay(5000).Wait(); using this one, it blocks the UI thread
//await Task.Delay(5000); using this one, it doesn't execute the task after delay
if (cancelToken.IsCancellationRequested)
{
return null;
}
HttpResponseMessage response = await hc.GetAsync(new Uri("http://google.com/"));
response.EnsureSuccessStatusCode();
string responseData = await response.Content.ReadAsStringAsync();
return responseData;
}
This is my async task and I have an issue trying to use delay inside of it. I have tried two methods and both seem to cause an issue with the task. Tried researching but couldn't find a way to fix my issue. Any help is appreciated
Other part of the code:
private async void Test()
{
string response = await httpClient(token);
Console.WriteLine("response: " + response);
}
private void button1_Click(object sender, EventArgs e)
{
Task t = new Task(Test);
t.Start();
Console.WriteLine("task started");
t.Wait();
Console.WriteLine("task finished");
}
The problem is here:
private async void Test()
{
string response = await httpClient(token);
Console.WriteLine("response: " + response);
}
As soon as you've made something async void you've completely removed any ability to track status. Your new Task(Test); is using new Task(Action), which will report completion as soon as the code first returns to the caller - i.e. at the first non-complete await (in your case: the Task.Delay). To do what you want, you should really be using the Task.Run(Func<Task>) API (or avoiding Task.Run / Task.Start completely, relying on the async plumbing itself), with a private async Task Test() method.
Your event handler could then be:
private async void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("about to start task");
var t = Task.Run(Test);
Console.WriteLine("task started");
await t;
Console.WriteLine("task finished");
}
or to avoid the extra thread (as noted by Nkosi):
private async void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("about to start task");
var t = Test();
Console.WriteLine("task started");
await t;
Console.WriteLine("task finished");
}
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();
}
I have been playing round with the Async CTP this morning and have a simple program with a button and a label. Click the button and it starts updating the label, stop the button it stops writing to the label. However, I'm not sure how to reset the CancellationTokenSource so that I can restart the process.
My code:
public partial class MainWindow : Window
{
CancellationTokenSource cts = new CancellationTokenSource();
public MainWindow()
{
InitializeComponent();
button.Content = "Start";
}
async Task DoWork(CancellationToken cancelToken)
{
int i = 0;
while (!cancelToken.IsCancellationRequested)
{
label.Content = i++.ToString();
await TaskEx.Delay(50, cancelToken);
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (button.Content == "Start")
{
button.Content = "Stop";
DoWork(cts.Token);
}
else
{
button.Content = "Start";
cts.Cancel();
}
}
}
You need to recreate the CancellationTokenSource - there is no way to "reset" this once you set it.
This could be as simple as:
private void Button_Click(object sender, RoutedEventArgs e)
{
if (button.Content == "Start")
{
button.Content = "Stop";
cts.Dispose(); // Clean up old token source....
cts = new CancellationTokenSource(); // "Reset" the cancellation token source...
DoWork(cts.Token);
}
else
{
button.Content = "Start";
cts.Cancel();
}
}
I had the same problem and I figured it out that the best way to solve it is to create cancellation token source newly just before calling the method.
this is what I do on my start button click:
cancellationTokenSource = new CancellationTokenSource();
cancellationToken = cancellationTokenSource.Token;
Task.Factory.StartNew(StartUpload, cancellationToken);
I change the caption for the same button to cancel and when a click occurs on cancel, I call
cancellationTokenSource.Cancel();
Here is the full code:
if (button3.Text != "&Start Upload")
{
cancellationTokenSource.Cancel();
}
else
{
try
{
cancellationTokenSource = new CancellationTokenSource();
cancellationToken = cancellationTokenSource.Token;
Task.Factory.StartNew(StartUpload, cancellationToken);
}
catch (AggregateException ex)
{
var builder = new StringBuilder();
foreach (var v in ex.InnerExceptions)
builder.Append("\r\n" + v.InnerException);
MessageBox.Show("There was an exception:\r\n" + builder.ToString());
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Now, with .NET 6 you can use TryReset() method which Attempts to reset the CancellationTokenSource to be used for an unrelated operation. see this issue and CancellationTokenSource.cs for more details.