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);
}
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()");
}
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();
}
I try to create a program checking in a lot of PDF. It can be done from a network drive and take few minutes. The application freeze all during the process and I want to avoid that.
I searched in a lot of posts and videos but I failed to implement it in my program. I tried this code to understand how it works but it failed too...
async private void button1_Click(object sender, EventArgs e)
{
rtbx.AppendText($"Processing...\n");
// This webswite takes 1-2s to be loaded
await HeavyWork(#"https://aion.plaync.com/");
rtbx.AppendText($"End.\n");
}
public Task HeavyWork(string url)
{
List<string> lesinfos = new List<string>();
while (checkBox1.Checked == false)
{
using (WebClient web1 = new WebClient())
{
lesinfos.Add(web1.DownloadString(url));
}
rtbx.AppendText($"{lesinfos.Count}\n");
this.Refresh();
}
rtbx.AppendText($"Done !\n");
return Task.CompletedTask;
}
When I click the button, I am never able to click in the checkbox, and the UI never respond.
Taking into account that you are forced to use a synchronous API, you can keep the UI responsive by offloading the blocking call to a ThreadPool thread. The tool to use for this purpose is the Task.Run method. This method is specifically designed for offloading work to the ThreadPool. Here is how you can use it:
public async Task HeavyWork(string url)
{
List<string> lesinfos = new List<string>();
using (WebClient web1 = new WebClient())
{
while (checkBox1.Checked == false)
{
string result = await Task.Run(() => web1.DownloadString(url));
lesinfos.Add(result);
}
rtbx.AppendText($"{lesinfos.Count}\n");
this.Refresh();
}
rtbx.AppendText($"Done !\n");
}
Notice the async keyword in the signature of the HeavyWork method. Notice the await before the Task.Run call. Notice the absence of the return Task.CompletedTask line at the end.
If you are unfamiliar with the async/await technology, here is a tutorial to get you started: Asynchronous programming with async and await.
Try using DownloadStringTaskAsync(...) with an `await (see example below).
public async Task HeavyWork(string url)
{
List<string> lesinfos = new List<string>();
while (checkBox1.Checked == false)
{
using (WebClient web1 = new WebClient())
{
var content = await web1.DownloadStringTaskAsync(url);
lesinfos.Add(content);
}
rtbx.AppendText($"{lesinfos.Count}\n");
this.Refresh();
}
rtbx.AppendText($"Done !\n");
}
note: WebClient is considered obsolete and it's now recommended to use HttpClient.
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);