I know this is not using the proper form, but can someone tell me why the first code snippet works and the 2nd causes a deadlock?
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = DownloadStringV3("https://www.google.com");
}
public string DownloadStringV3(String url)
{
var resp = new HttpClient().GetAsync(url).Result;
return resp.Content.ReadAsStringAsync().Result;
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = DownloadStringV3("https://www.google.com").Result;
}
public async Task<string> DownloadStringV3(String url)
{
var resp = await new HttpClient().GetAsync(url);
return await resp.Content.ReadAsStringAsync();
}
Different synchronization contexts? This is using .NET 4.8
Every time you await something, you say, I am temporary done here, give some other thread a chance to execute and then continue from here, when the async task is completed.
So in your case await new HttpClient().GetAsync(url) switches the the ui thread and waiting for DownloadStringV3("https://www.google.com").Result be done, which will never be the case, because we do never execute return await resp.Content.ReadAsStringAsync().
Thats why you should do async all the way to the root and never call Result.
The first code works, because you block on the UI thread and don't try to switch to another thread, while waiting for it.
To make the second one working you need to:
private async void button1_Click(object sender, EventArgs e)
{
// make with try catch sure, you catch any error, async void is special. Better is async Task
textBox1.Text = await DownloadStringV3("https://www.google.com");
}
public async Task<string> DownloadStringV3(String url)
{
var resp = await new HttpClient().GetAsync(url);
return await resp.Content.ReadAsStringAsync();
}
Related
When I try to read the content of one web page loaded into the webview2 control, task ExecuteScriptAsync blocks forever. After this the application does not respond, but the site is still operational. The site is posted on an corprate intranet, so I can't provide the URL here. It is Ivanti Service Desk.
private void bnNewReguest_Click(object sender, EventArgs e)
{
var t = GetTextAsync();
string sHtml = t.Result;
if (!sHtml.Contains("shortcutItem_12345"))
{
MessageBox.Show("Please wait for the page to load");
return;
}
webView21.ExecuteScriptAsync("document.getElementById('shortcutItem_12345').click()");
}
private async Task<string> GetTextAsync()
{
if (webView21.CoreWebView2 == null)
{
MessageBox.Show("Wait a moment...");
return "";
}
var script = "document.documentElement.outerHTML";
string sHtml = await webView21.CoreWebView2.ExecuteScriptAsync(script); // deadlock
string sHtmlDecoded = System.Text.RegularExpressions.Regex.Unescape(sHtml);
return sHtmlDecoded;
}
I also tried the code below, but the result is similar.
string sHtml = await webView21.CoreWebView2.ExecuteScriptAsync(script).ConfigureAwait(false);
The WebView2 version is 1.0.1418.22. How can I protect from deadlock?
I found a thread about the same problem here, but none of the solutions work for me.
I describe this deadlock on my blog. The best solution is to not block on asynchronous code.
In your case, this could look like this:
private async void bnNewReguest_Click(object sender, EventArgs e)
{
string sHtml = await GetTextAsync();
if (!sHtml.Contains("shortcutItem_12345"))
{
MessageBox.Show("Please wait for the page to load");
return;
}
await webView21.ExecuteScriptAsync("document.getElementById('shortcutItem_12345').click()");
}
My MainPage code-behind:
private void Button_Clicked(object sender, EventArgs e)
{
Query().Wait();
App.Current.MainPage = new Categories();
}
public async Task Query()
{
restaurantsClient = (App.Current as App).restaurantsClient;
try
{
var restaurantsNames = await restaurantsClient.GetCatalogsAsync(1);
}
catch (Exception ex)
{
var x = 0;
}
}
I tried this code too but didn't work, happens the same problem:
async static Task GetRequest(String URL)
{
using (HttpClient client = new HttpClient())
{
// As soon as I try to step over (or even into) this next line, it crashes.
using (HttpResponseMessage response = await client.GetAsync(URL))
{
using (HttpContent content = response.Content)
{
string data = await content.ReadAsStringAsync();
Console.WriteLine(data);
}
}
}
}
Rest-API in C#:
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
When the project reach this line it just die without showing any error.
If I do it in the browser it works.
The API is running locally in my PC (Im using Conveyor to expose the API).
Ok I make a video to see better what Im talking about:
https://youtu.be/ONKTipPsEXI
As you can see after I click Next Step in response line stop executing the rest of the code.
That's because you're using .Wait() of a Task on the UI thread (Button.Clicked event is handled on the UI thread) causing a deadlock. The task is waiting for the UI thread to give it control and the UI thread is waiting for the task to complete. The solution to this is to add async modifier to your event handler and use await Query() instead of Query().Wait().
private async void Button_Clicked(object sender, EventArgs e)
{
await Query();
App.Current.MainPage = new Categories();
}
I'd also recommend reading this article by Stephen Cleary about this matter. Moreover, he's made a fabulous series (A Tour of Task) about C# tasks in general.
UPDATE:
After OP's question update and discussion in this answer's comments; he thinks that there's a problem because he can reach the end of GetCatalogsAsync(int) before the end of GetCatalogsAsync(int, CancellationToken). That's completely natural and is to be expected. Solution:
public async System.Threading.Tasks.Task<CatalogsInCategory> GetCatalogsAsync(int id)
{
return await GetCatalogsAsync(id, System.Threading.CancellationToken.None);
}
Here is a question related to my question i found, which did not work.
In my Page load method of web forms I want to call async method
void Page_Load(object sender, EventArgs e)
I want to call async method because I want to be able to call the GetUserInfoAsync method of IdentityModel
This is what I have
protected void Page_Load(object sender, EventArgs e)
{
var token = HttpContext.Current.Request.Headers.Get("Authorization");
GetUserClaims(token).Wait();
}
public async Task GetUserClaims(string token)
{
var client = new HttpClient();
var response = await client.GetUserInfoAsync(new UserInfoRequest
{
Address = "https://localhost:44367/connect/userinfo",
Token = token,
});
var result = response.Claims;
}
Current problem is I never reach result = response.claims part.
GetUserClaims(token).Wait();
was based upon the answer to the question I linked above.
The other option I tried was use the PageAsyncTask and RegisterAsyncTask as mentioned in one of the option in the answer.
PageAsyncTask t = new PageAsyncTask(GetUserClaims(token));
but I get red squiggly thing which complains saying
cannot convert from System.Threading.Task.Task to System.Func<System.Threading.Task.Task>
Actually this helped
protected void Page_Load(object sender, EventArgs e)
{
var token = HttpContext.Current.Request.Headers.Get("Authorization");
PageAsyncTask t = new PageAsyncTask(() => GetUserClaims(token));
// Register the asynchronous task.
Page.RegisterAsyncTask(t);
// Execute the register asynchronous task.
Page.ExecuteRegisteredAsyncTasks();
//GetUserClaims(token).ConfigureAwait(false).GetAwaiter().GetResult();
}
If you absolutely can't make the Page_Load event handler async void, as is suggested by the linked question, then the proper way would be:
GetUserClaims(token).ConfigureAwait(false).GetAwaiter().GetResult();
To avoid the deadlock which happens when you call Wait().
You would also need to add .ConfigureAwait(false) to all nested async calls to ensure that the execution resumes in the same context:
UserInfoResponse response = await client.GetUserInfoAsync(new UserInfoRequest
{
Address = "https://localhost:44367/connect/userinfo",
Token = token,
}).ConfigureAwait(false);
This question already has answers here:
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on
(22 answers)
Closed 6 years ago.
This is the code, i want my form keep be alive while downloading code from internet and when its finish i want to update html container. When i do this form freeze. I am new to threading and tasking and i need little help. Thanks!
private void buttonGetHTML_Click(object sender, EventArgs e)
{
Task.Run(() => Download(textBoxUrl.Text)).ContinueWith((Download) => htmlContent.Text = Download.Result);
}
private string Download(string url)
{
using (WebClient client = new WebClient())
{
string context = client.DownloadString(url);
return context;
}
}
Use this:
private async void buttonGetHTML_Click(object sender, EventArgs e)
{
using (WebClient client = new WebClient())
{
string context = await client.DownloadStringAsync(textBoxUrl.Text);
htmlContent.Text = content
}
}
Most people try to solve this with Control.Invoke or something (see link in comment) but in this case there is a better option. Using the Async methods of the WebClient class. See https://msdn.microsoft.com/en-us/library/system.net.webclient.downloadstringasync(v=vs.110).aspx
Always look for Async methods first, before starting your own thread instead.
If you cannot use async await try this:
private void buttonGetHTML_Click(object sender, EventArgs e)
{
var url = textBoxUrl.Text;
Task.Run(() => Download(url)).ContinueWith((Download) => { htmlContent.Text = Download.Result; }, TaskScheduler.FromCurrentSynchronizationContext());
}
private string Download(string url)
{
using (WebClient client = new WebClient())
{
string context = client.DownloadString(url);
return context;
}
}
So the call that accesses the UI is out of the Task and the continuation that accesses the UI will run on the UI thread.
The reason is calling textBoxUrl.Text from non-UI thread. Try to retrieve the text first, then call Task.Run:
private void buttonGetHTML_Click(object sender, EventArgs e)
{
var url = textBoxUrl.Text;
Task.Run(() => Download(url)).ContinueWith((Download) => htmlContent.Text = Download.Result);
}
Trying to use Async using Microsoft.Bcl on .net 4.0 in Visual Studio 2012. Results does not show up.
private void button3_Click(object sender, EventArgs e)
{
byte[] resultBytes;// = ReceiveStringAsync().Result;
Task<byte[]>[] tasks = new Task<byte[]>[1];
tasks[0] = ReceiveStringAsync();
Task.WaitAll(tasks, -1);
resultBytes = tasks[0].Result;
MessageBox.Show("Async Mode called in sync - " + data.Length.ToString());
}
public async Task<byte[]> ReceiveStringAsync()
{
string strURL = #"https://testurl.com";
WebClient client = new WebClient();
byte[] data = await client.DownloadDataTaskAsync(strURL).ConfigureAwait(true);
return data;
}
This is the problem, which has nothing to do with the fact that you're using .NET 4:
Task.WaitAll(tasks, -1);
That will block until everything in tasks completes. At that point, your UI thread can't do any more work... but it has to do more work in order to resume the code within ReceiveStringAsync after the await expression. So your task can't complete until Task.WaitAll has completed, which can't complete until the task has completed, and you have a deadlock.
The lesson here is never to use blocking calls like Task.WaitAll (or Task.Result or Task.Wait()) within the UI thread.
The solution is to make button3_Click async as well, so you can await the Task<byte[]> returned by ReceiveStringAsync.
private async void button3_Click(object sender, EventArgs e)
{
byte[] resultBytes = await ReceiveStringAsync();
// Not clear what data is here, but that's a different matter...
MessageBox.Show("Async Mode called in sync - " + data.Length.ToString());
}
As an aside, it's really odd for a ReceiveStringAsync method to return a Task<byte[]> instead of a Task<string>...