EvaluateScriptAsync hanging - c#

In CefSharp WinForms, I'm trying to get the html source of the page using JS once the page has loaded, however the application is freezing. I'm using a BackgroundWorker and the concerned functions are as follows:
void bw_DoWork(object sender, DoWorkEventArgs e)
{
browser.Load("http://www.google.com");
browser.FrameLoadEnd += delegate
{
object js = EvaluateScript(browser, "1+1");
MessageBox.Show(js.ToString());
};
}
object EvaluateScript(ChromiumWebBrowser b, string script)
{
var task = b.EvaluateScriptAsync(script);
task.Wait();
return task.Result;
}

As amaitland pointed out, FrameLoadEnd was causing the hang by running in the UI thread. The below code is working:
void bw_DoWork(object sender, DoWorkEventArgs e)
{
first.Load("http://www.google.com");
browser.FrameLoadEnd += delegate
{
Task task = new Task(() => {
object js = EvaluateScript(browser, "document.getElementsByTagName('html')[0].innerHTML;");
MessageBox.Show(js.ToString());
});
task.Start();
};
}
static object EvaluateScript(ChromiumWebBrowser b, string script)
{
var task = b.EvaluateScriptAsync(script);
task.Wait();
JavascriptResponse response = task.Result;
return response.Success ? (response.Result ?? "") : response.Message;
}

Whilst you assign FrameLoadEnd in the BackgroundWorker thread, it's actually executed on the underlying CEF UI thread, for which you cannot block without issues.
I'd typically suggest you spawn a Task from within the event handler to complete your work.

As a general rule, it's a bad idea to use Task.Wait on async code; rather, you should use async "all the way down". See also Don't Block on Async Code by Stephen Cleary. The short answer is that if you do this in an application with a synchronization context, you can cause a circular wait for the synchronization context (and hence a deadlock). The article I linked to has several examples of this, but I'd strongly suggest replacing Task.Wait with await here if possible.

Related

C# - ' Dispatcher.BeginInvoke(new Action(() => ' doesn't work within Winforms. CS0120 [duplicate]

I am trying to understand better how can I update a windows forms progress bar from an async operation but I am getting some unexpected behavior from that.
Basically I am having a button which should after is being clicked to update a progress bar and then set it back to 0 once the progress bar gets 100% updated.
This is my code:
private async void button1_Click(object sender, EventArgs e)
{
await CallMethodAsync().ContinueWith((prevTask) =>
{
prevTask.Wait();
progressBar1.Invoke(new Action(() => { progressBar1.Value = 0; }));
});
}
private static async Task ExecuteMethodAsync(IProgress<double> progress = null)
{
double percentComplete = 0;
bool done = false;
while (!done)
{
if (progress != null)
{
progress.Report(percentComplete);
}
percentComplete += 10;
if(percentComplete == 100)
{
done = true;
}
}
}
private async Task CallMethodAsync()
{
var progress = new Progress<double>();
progress.ProgressChanged += (sender, args) => { progressBar1.Increment(10); };
await ExecuteMethodAsync(progress);
}
Having this implementation the progress bar is not being updated at all even if I call "Wait()" on the operation that should update the value of the progress bar.
If i remove this part of code:
progressBar1.Invoke(new Action(() => { progressBar1.Value = 0; }));
the progress bar gets updated but it remains all the time like that, and I want to set it back to 0 once it was entirely filled so that I can update it again when I click again the button.
Could someone please explain me what am I doing wrong ?
One of the reasons async-await syntax was invented because it was difficult to follow the sequence of instructions when tasks were concatenated using functions like ContinueWith.
If you use async-await it is seldom necessary to use statements like ContinueWith. After an await, the thread already continues with the statements after the await.
If the button is clicked, you want to call ExcecuteMethodAsync. This function takes an IProgress, because it wants to report progress regularly. You want to call this function asynchronously, so whenever the function has to wait for something, it doesn't really wait, but returns control to you so you could do other things instead of really waiting, until you encounter an await, in which case your caller continues processing until he encounters an await, etc.
The nice thing with async-await is that the thread that continues after your call to an async function has the same context as the calling thread. This means that you can regard it as your original thread. No InvokeRequired, no need to protect data with mutexes etc.
Your function could be simplified as follows:
async Task CallMethodAsync()
{
var progress = new Progress<double>();
progress.ProgressChanged += OnProgressReported;
await ExecuteMethodAsync(progress);
}
private void OnProgressReported(object sender, ...)
{
// because this thread has the context of the main thread no InvokeRequired!
this.progressBar1.Increment(...);
}
private async void button1_Click(object sender, EventArgs e)
{
await CallMethodAsync();
}
So when the button is clicked, CallMethodAsync is called. This function will create A Progress object and subscribes on its Report event. Note that this is still your UI-thread. Then it calls ExecuteMethodAsync, which will regularly raise event Report, which is handled by OnProgressReported.
Because ExecuteMethodAsync is async, you can be sure there is somewhere an await in it. This means that whenever it has to await, control returns to the caller, which is CallMethodAsync, until is encounters an await, which in this case is immediately.
Control goes up the call stack to the caller, which is button1_click, where it immediately encounters an await, so control goes up the call stack, etc.
All these controls have the same context: it is as if they are the same thread.
An article that helped me a lot to understand async-await is this interview with Eric Lippert. Search somewhere in the middle for async await
Another articel that helped me a lot to learn good practices were this article by the ever so helpful Stephen Cleary and Async/Await - Best Practices in Asynchronous Programming also by Stephen Cleary
Your issue is happening because ExecuteMethodAsync(...) is not actually asynchronous.
Add the following before the while loop to make it asynchronous
await Task.Delay(1);
or enclose some synchronous portion of code (e.g. the while loop) into a:
await Task.Run(() => { ... });
or (the best one), add the following at the beginning of the function:
await Task.Yield(); // Make us async right away

C# - Cross Thread Error using Async and Await on TextBox

I am new to Async and Await and have created a simple project in order to understand how it works.
For this, I have a simple Windows Form application that has 2 elements:
Get Completed Items button
TextBox showing all Completed Items retrieved
When I click the button, it should display all completed Items in the TextBox.
This is the code I have written:
private async void btnGetCompletedItems_Click(object sender, EventArgs e)
{
QueueSystem queueSystem = QueueSystem.NewInstance(75);
Stopwatch watch = new Stopwatch();
watch.Start();
await Task.Run(() => GetCompletedItems(queueSystem));
watch.Stop();
lblTime.Text = $"{watch.ElapsedMilliseconds.ToString()} ms";
}
private void GetCompletedItems(QueueSystem queueSystem)
{
foreach (var item in queueSystem.GetCompletedItems())
{
txtItems.Text += $"{txtItems.Text}{item.ItemKey}{Environment.NewLine}";
}
}
However, I am getting an error in
txtItems.Text +=
$"{txtItems.Text}{item.ItemKey}{Environment.NewLine}";
The error says
Additional information: Cross-thread operation not valid: Control
'txtItems' accessed from a thread other than the thread it was created
on.
I checked in Debug and a new thread was created for GetCompletedItems(). When I read about Async and Await, I read that it doesn't necessarily create a new thread but it seems to have created a new one for some reason.
Is my implementation and understanding of Async and Await wrong?
Is it possible to use Async and Await in a Windows Forms application?
You cannot access UI thread on a different thread. This should help
private async void btnGetCompletedItems_Click(object sender, EventArgs e)
{
QueueSystem queueSystem = QueueSystem.NewInstance(75);
Stopwatch watch = new Stopwatch();
watch.Start();
var results = await Task.Run(() => queueSystem.GetCompletedItems());
foreach (var item in results)
{
txtItems.Text += $"{txtItems.Text}{item.ItemKey}{Environment.NewLine}";
}
watch.Stop();
lblTime.Text = $"{watch.ElapsedMilliseconds.ToString()} ms";
}
You can access the thread from another thread in a following way. It does helps to avoid the cross thread exception in your application.
private void Thread()
{
this.Invoke((System.Action)(() => {
//your thread call or definition
});
}
When I read about Async and Await, I read that it doesn't necessarily create a new
thread
This is true for regular async methods. Consider this:
private async void button1_Click(object sender, EventArgs e)
{
Trace.WriteLine(Thread.CurrentThread.ManagedThreadId);
await DoesNothing();
}
private async Task DoesNothing()
{
// outputs the same thread id as similar line as from above;
// in particlar, for WinForms this means, that at this point
// we are still at UI thread
Trace.WriteLine(Thread.CurrentThread.ManagedThreadId);
await Task.Delay(1);
}
but it seems to have created a new one for some reason
This is what Task.Run is intended for:
Queues the specified work to run on the ThreadPool
In other words, it pushes anything you pass it as a delegate to a thread pool thread. Since we are in WinForms, this means, that anonymous method () => GetCompletedItems(queueSystem) will be executed at thread pool thread, not at UI one.
Here's code sample from above with little change:
private async void button1_Click(object sender, EventArgs e)
{
Trace.WriteLine(Thread.CurrentThread.ManagedThreadId);
await Task.Run(DoesNothing);
}
private async Task DoesNothing()
{
// outputs DIFFERENT thread id;
// in particlar, for WinForms this means, that at this point
// we are not at UI thread, and we CANNOT access controls directly
Trace.WriteLine(Thread.CurrentThread.ManagedThreadId);
await Task.Delay(1);
}

Prevent UI from freezing when using Task.Result

I am calling Task.Run(() => DoSomething()).Result which causes the UI to freeze and it happens because am using ".Result". I need Result because i want to return the value.
I don't want the Method StartSomething to be async because I don't want to await the method StartSomething. I want the await to happen at DoSomething().
So basically I need a asynchronous method to be called by a synchronous method, without freezing the UI. Plus I want to return the value from the async method to the top level that is on Button Click.
Can this code be improved or is there any other solution?
private TaskCompletionSource<bool> TaskCompletion = null;
private void Button_Click(object sender, RoutedEventArgs e)
{
bool k = StartSomething();
}
private bool StartSomething()
{
return Task.Run(() => DoSomething()).Result;
}
private async Task<bool> DoSomething()
{
TaskCompletion = new TaskCompletionSource<bool>();
await Task.WhenAny(TaskCompletion.Task, Task.Delay(3000));
MessageBox.Show("DoSomething");
return true;
}
Method StartSomething() doesn't make sense to me. It starts a new Task and then just synchronously waits for the result (.Result) of this task, which is effectively useless - it is nearly [*] the same as calling DoSomething() directly. Also DoSomething() is already asynchronous so you don't need to start a new Task for it.
It looks like you don't need StartSomething() method at all. If you make Button_Click handler async, you can then simply await DoSomething() directly:
private TaskCompletionSource<bool> TaskCompletion = null;
private async void Button_Click(object sender, RoutedEventArgs e)
{
bool k = await DoSomething();
}
private async Task<bool> DoSomething()
{
TaskCompletion = new TaskCompletionSource<bool>();
await Task.WhenAny(TaskCompletion.Task, Task.Delay(3000));
MessageBox.Show("DoSomething");
return true;
}
Edit:
While using async all the way down solution (as shown above) is IMO the preferred way, if you really can't change calling code to async, I can think of two ways to call async method from synchronous method without blocking UI. First is to manually set up a continuation tasks like this:
private void Button_Click(object sender, RoutedEventArgs e)
{
DoSomething().ContinueWith((task) =>
{
bool k = task.Result;
// use the result
},
// TaskScheduler argument is needed only if the continuation task
// must run on the UI thread (eg. because it access UI elements).
// Otherwise this argument can be omitted.
TaskScheduler.FromCurrentSynchronizationContext());
// Method can exit before DoSomething().Result becomes
// available, which keep UI responsive
}
So you basicly split synchronous method (one split instead of each await) into several parts (continuation lambda methods) linked by .ContinueWith. This is similar to what await does under a hood. Problem is that unlike await (which produces nice and clean code), your code will be full of these continuation lambdas. And it will get much worse when you add exception handling blocks, using blocks, etc.
The second approach is using nested loops, eg. Stephen Toub's WaitWithNestedMessageLoop extension method:
static T WaitWithNestedMessageLoop<T>(this Task<T> task)
{
var nested = new DispatcherFrame();
task.ContinueWith(_ => nested.Continue = false, TaskScheduler.Default);
Dispatcher.PushFrame(nested);
return task.Result;
}
Nested loops are quite advanced technique (I actually never used it) and I don't recommend using it unless you have to.
[*] There are differences in exception handling, executing thread, etc., but these are not relevant to this question.

How do I prevent webcontrol from hanging in a while loop? [duplicate]

i was doing some processor heavy task and every time i start executing that command my winform freezes than i cant even move it around until the task is completed. i used the same procedure from microsoft but nothing seem to be changed.
my working environment is visual studio 2012 with .net 4.5
private async void button2_Click(object sender, EventArgs e)
{
Task<string> task = OCRengine();
rtTextArea.Text = await task;
}
private async Task<string> OCRengine()
{
using (TesseractEngine tess = new TesseractEngine(
"tessdata", "dic", EngineMode.TesseractOnly))
{
Page p = tess.Process(Pix.LoadFromFile(files[0]));
return p.GetText();
}
}
Yes, you're still doing all the work on the UI thread. Using async isn't going to automatically offload the work onto different threads. You could do this though:
private async void button2_Click(object sender, EventArgs e)
{
string file = files[0];
Task<string> task = Task.Run(() => ProcessFile(file));
rtTextArea.Text = await task;
}
private string ProcessFile(string file)
{
using (TesseractEngine tess = new TesseractEngine("tessdata", "dic",
EngineMode.TesseractOnly))
{
Page p = tess.Process(Pix.LoadFromFile(file));
return p.GetText();
}
}
The use of Task.Run will mean that ProcessFile (the heavy piece of work) is executed on a different thread.
You can also do this by starting your task in new thread.
Just use Thread.Start or Thread. ParameterizedThreadStart
See these for your reference:
http://msdn.microsoft.com/en-us/library/system.threading.parameterizedthreadstart.aspx
Start thread with parameters
You could use BackgroundWorker component.

How to replace (async) BackgroundWorker with (async) Task.Run (of TPL)?

I've asked a question on how to know when a string of another other party library code changes in my code. I can get access to the string itself at any time. but can't implement INotifyPropertyChanged since it's not my code.
I was offered to use a BackgroundWorker and this solution does work for me!
but, I was trying to make sure it is the best solution and got an advise to look at TPL, further researching showed that Task.Run of TPL might be a better solution, as mentioned here for example: Task parallel library replacement for BackgroundWorker? but I couldn't implement it in code.
I am trying to replace this code by Task.Run (Thanks to #a.azemia)
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (s, e) =>
{
while (true)
{
if (!fc.SecondString.Equals(AnotherPartyLibrary.firstString))
{
fc.SecondString = AnotherPartyLibrary.firstString;
}
Thread.Sleep(1000);
}
};
bw.RunWorkerAsync();
I couldn't find any example that fit my scenario and tried to learn from other examples with no success. I need a while loop inside the task and it needs to run asynchronously like the BackgroundWorker does.
couldn't find any example with a while loop in the task, so I am not sure of how this can be done.
I've also read that Lambda expressions uses more resources and it was shown in some test that I've seen so I would've liked to avoid Lambda if possible.
Thanks for the help!
Try this (untested):
public async Task DoWork()
{
while (true)
{
if (!fc.SecondString.Equals(AnotherPartyLibrary.firstString))
{
fc.SecondString = AnotherPartyLibrary.firstString;
}
await Task.Delay(1000);
}
}
I've used Task.Delay instead of Thread.Sleep as the former does not block a thread while delay is happening. You can invoke this function with await
await DoWork();
I still see no real benefit, but here you go:
private Task T;
private void Form1_Load(object sender, EventArgs e)
{
// ... make sure your string stuff is setup first ...
T = Task.Run(delegate() {
while (true)
{
// ... code ...
System.Threading.Thread.Sleep(1000);
}
});
}

Categories

Resources