How can I use async to increase WinForms performance? - c#

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.

Related

C# Task.Wait Freezin

I have a winform when you click a button it calls a task wait function however it is freezing i'm not sure why.
private async Task GetsData()
{
var connectionString = "mongodb://localhost:27017";
var client = new MongoClient(connectionString);
IMongoDatabase db = client.GetDatabase("mydb");
var collection = db.GetCollection<BsonDocument>("mydata");
using (var cursor = await collection.Find(new
BsonDocument()).ToCursorAsync())
{
while (await cursor.MoveNextAsync())
{
foreach (var doc in cursor.Current)
{
MessageBox.Show(doc.ToString());
}
}
}
}
private void button1_Click(object sender, EventArgs e)
{
try
{
GetsData().Wait();
}
catch(Exception err)
{
MessageBox.Show(err.ToString());
}
}
how can `i stop it from freezing?
Task.Wait() is a blocking action. It will block the GUI thread waiting for the Task/Promise returned from GetsData() to resolve. Meanwhile, async scheduling in a WinForms app will be scheduled against the GUI thread by the SynchronizationContext. Since the GUI thread is blocked by the Wait(), these units of work cannot execute in order to resolve the Task which results in a deadlock.
When using async-await, it is best to use all the way through your code. It is very easy to end up with a deadlock when doing any thread blocking with async-await. Stephen Cleary has some great articles around this when async-await was first introduced.
To resolve, there are a few potential paths but the simplest for this case is likely to turn your event handler in to an async void method and await the GetsData() call instead of using Wait(). Event handlers are the one generally accepted place async void is ideal to use.
private async void button1_Click(object sender, EventArgs e)

Doing synchronous calls when dispatcher's PushFrame is used

I'm working on a plugin for .Net WPF application which uses the frame pushing technique to achieve 'responsible' UI. Each long-running task in its SDK calls the code:
public static void Wait(Task task)
{
DispatcherFrame nestedFrame = new DispatcherFrame();
task.ContinueWith(delegate { return nestedFrame.Continue = false; });
Dispatcher.PushFrame(nestedFrame);
task.Wait();
}
So the typical SDK's method can be emulated by the following code:
private void SdkMethod()
{
var t = Task.Factory.StartNew(() =>
{
Thread.Sleep(1500);
});
Wait(t);
}
It causes a situation when I can not do two synchronous calls to the SDK's methods from the UI thread:
private void OnClick(object sender, RoutedEventArgs e)
{
this.sdk.SdkMethod();
this.sdk.SdkMethod();
}
Is there any way to synchronize calls in such a situation? I cannot modify SDK, only my own calls to it.
If I understood correctly from reading the post and comments, you want to call SdkMethod and have it act synchronously. It's being called without letting you know when it is done.
If it is a void method and it isn't a task, you aren't going to get anything in return. I tried to listen to the Dispatcher Hooks for completion, but that was not helpful.
I'd create an aysnc Task method that has a Task.Delay then await it between calls. That way you aren't interrupting the user, but you still force the call to be paused until you call the second method.
private void OnClick(object sender, RoutedEventArgs e)
{
this.sdk.SdkMethod();
await DelayAsync(3).ConfigureAwait(false);
this.sdk.SdkMethod();
}
private async Task DelayAsync(int seconds)
{
await Task.Delay(seconds * 1000).ConfigureAwait(false);
}
I hope this helps.

Returning / setting a value within a thread

I'm trying to open an OpenFileDialog within C# (codebehind, on an asp.net page). Because the regular references and the system.windows.form ones have some conflicts, I'm having to use the OpenFileDialog box within a thread, as below:
protected void Button1_Click(object sender, EventArgs e)
{
Thread newThread = new Thread(new ThreadStart(BrowseForFile));
newThread.SetApartmentState(ApartmentState.STA);
newThread.Start();
}
static void BrowseForFile()
{
System.Windows.Forms.OpenFileDialog MyFile = new System.Windows.Forms.OpenFileDialog();
if (MyFile.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
}
}
The way the page works means that this has to be in C# - using asp.net's fileupload won't work.
Now, the OpenFileDialog appears fine, and I can get values from it, but I ideally need to pass values into the thread (the BrowseForFile) and have it work with other controls on the page, in order to set things up. However, I'm very new to using threads.
Can someone show me, basically, how to get an integer from the Button1_Click into the BrowseForFile, and how I would set a label on the page from BrowseForFile?
If you use a modern version of .net you could use async-await for this. This makes it much easier for you to communicate with your dialog and to pass data to the thread that performs your background work.
To be able to use async-await:
Declare your function async
Let your function return Task instead of void and Task<TResult> instead of TResult.
There is one exception: event handlers may return void
In your async function start your other threads using Task.Run
while the other thread is running you can do other things
if you need the result: call await Task.
In your case, you'll have to change your thread class into a procedure that contains the code in your thread class. This procedure may be in any class. It must be declared async and return Task instead of void:
Of course you'll have to change your thread class into an async procedure:
private async Task MyThreadProcedureAsync(string fileName)
{
// do something really slow with fileName and return void
}
protected async void Button1_Click(object sender, EventArgs e)
{
string fileName = this.BrowseForFile();
if (!String.IsNullOrEmpty(fileName))
{
var myTask = Task.Run( () => MyThreadProcedureAsync(fileName))
// if desired do other things.
// before returning make sure the task is ready:
await myTask;
// during this wait the UI keeps responsive
}
}
private string BrowseForFileName()
{
using (var dlg = new System.Windows.Forms.OpenFileDialog())
{
// if needed set some properties; show the dialog:
var dlgResult = dlg.ShowDialog(this);
if (dlgResult == System.Windows.Forms.DialogResult.OK)
{
return dlg.FileName;
}
else
{
return null;
}
}
}

Calling an async method with c#5.0

I do some tests with the new asynchronous pattern of C# 5.0 (async/await) I have a problem with understanding how the asynchronous methods are called.
Considering this code :
private async Task<string> DownloadAsync()
{
progress.ProgressChanged += (s, e) =>
{
progressBar1.Value = e.value;
};
return await DownloadSomething(myurl, progress);
}
private async void CallDownloadAsync()
{
string text = await DownloadAsync();
progressBar1.Value = 0;
label1.Text = "Done!";
}
private void button4_Click(object sender, EventArgs e)
{
CallDownloadAsync();
}
So, this code works very well. When I clic the "button4" a downloading task begins and my ProgressBar is updated correctly.
But, I'd like to compact my code a little bit more by removing CallDownloadAsync() method like this :
private void button4_Click(object sender, EventArgs e)
{
new Action(async () =>
{
string result = await Task.Run<string>(() => DownloadAsync());
}).Invoke();
label1.Text = "Running...";
}
So here, I want to directly initiate an action which calls the DownloadAsync method but when I hit my Button4 I have a Cross-thread operation not valid on the progressBar. So I do not understand what is the main difference between the Action() and the call of my CallDownloadAsync() method.
You may find my async/await intro helpful. In particular, an async method does not run on a background thread; Task.Run is used to run something on a background thread, hence the difference in your code.
In general, you should avoid async void, unless you're writing an async event handler. Like this:
private async void button4_Click(object sender, EventArgs e)
{
label1.Text = "Running...";
string result = await DownloadAsync();
progressBar1.Value = 0;
label1.Text = "Done!";
}
The difference is that in former case you call CallDownloadAsync() from UI thread (context).
In the latter case, DownloadAsync() is called from the initiated Task which is generally executed in a different thread created by TPL (Task Parallel Library) out of UI thread or threads created from it.
In WPF, UI components can be accessed only by a dedicated UI thread or (its children) threads created from under it (i.e with the same UI context).

How do I force a task to run on the UI thread?

Original message below. Let me try and explain with more details why I am asking for this.
I have a page that listens to the Share charm request:
void Page_Loaded(object sender, RoutedEventArgs e)
{
m_transferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.GetForCurrentView();
m_transferManager.DataRequested += TransferManager_DataRequested;
}
When the event fires (TransferManager_DataRequested) it does not fire on the UI thread:
void TransferManager_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
var data = args.Request.Data;
// More related stuff omitted - not important.
data.SetDataProvider(StandardDataFormats.Bitmap, GetImage_DelayRenderer);
}
Now, when GetImage_DelayRender is called, it also does not get called on the UI thread. However, in it, I need to do a bunch of UI related things. Specifically, I need to call a method that only works on the UI (it's a method I use elsewhere and I want to reuse it's logic). The method is called GetImageAsync and it needs to run on the UI because it does a bunch of interactions with WriteableBitmap. It also does a bunch of async operations (such as writing to stream etc) which is why it's async. I block the UI on GetImageAsync() for as short a time as I can.
Here's what GetImage_DelayRender looks like:
private async void GetImage_DelayRenderer(DataProviderRequest request)
{
var deferral = request.GetDeferral();
await Dispatcher.RunTask(async () => // RunTask() is an extension method - described in the original question below.
{
try
{
var bitmapStream = await GetImageAsync();
request.SetData(RandomAccessStreamReference.CreateFromStream(bitmapStream));
}
catch
{
}
});
deferral.Complete();
}
What I want to know is, what is the most correct way to achieve the call to Dispatcher.RunTask() above (which is my hack extension method).
----- START ORIGINAL MESSAGE -------
Say I have the following task:
private async Task SomeTask()
{
await Task.Delay(1000);
// Do some UI and other stuff that may also be async
}
Edit (Clarification): I do not want to block the UI. The task I want to execute (even in the example, if you read it) WILL NOT block the UI. I just want the task to run in the context of the UI for it's synchronous portions.
I want to run this on code on the UI thread as an Async operation. Dispatcher.RunXXX() methods take an action, which means they will run the action and notify you when they are done. That's not good enough. I need the entire task to run on the UI thread (as it would have executed had I run it from the UI thread) and then, when done, to notify me back.
The only way I could think of, is to use the Dispatcher.RunXXX() methods to execute an anon delegate that sets a local variable in my method to the task and then awaits that...
public async static Task RunTask(this CoreDispatcher dispatcher, Func<Task> taskGiver)
{
Task task = null;
await dispatcher.RunAsync(() => task = taskGiver());
await task;
}
This looks pretty damn ugly. Is there a better way of doing it?
Edit2: Guys - read this code - if I execute the first code block above using the RunTask() hack I have, IT WILL NOT BLOCK THE UI on the Task.Delay()...
I want to run this on code on the UI thread as an Async operation.
Then just run it:
async void MyEventHandler(object sender, ...)
{
await SomeTask();
}
Update:
I'm not sure this is a "legal" operation, but you can schedule that method to run on the UI thread by capturing the CoreDispatcher while the UI is active and later calling RunAsync:
private async void GetImage_DelayRenderer(DataProviderRequest request)
{
var deferral = request.GetDeferral();
Task task = null;
await coreDispatcher.RunAsync(() => { task = SomeTask(); });
await task;
deferral.Complete();
}
I don't have time to do a complete solution, so hopefully you will still find this useful...
First, as others have pointed out, you cannot run something on the UI thread and not have it block the UI thread. End of discussion. What you are saying you need is something to run on a non-UI thread and periodically notify the UI thread that there are updates that need to be processed.
To accomplish this, you need something like this...
public class LongTask
{
public event EventHandler MyEvent;
public void Execute()
{
var task = Task.Factory.StartNew(() =>
{
while (true)
{
// condition met to notify UI
if (MyEvent != null)
MyEvent(this, null);
}
});
}
}
In your UI then, do something like...
private void button_Click(object sender, RoutedEventArgs e)
{
var test = new LongTask();
test.MyEvent += test_MyEvent;
test.Execute();
}
void test_MyEvent(object sender, EventArgs e)
{
Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
test.Text += " bang ";
});
You could obviously implement this in a much cleaner fashion using something like MVVM, but this is the basic idea.
}
I've done it like this:
public static Task<string> GetResultAsync()
{
return Task<string>.Factory.StartNew(() => GetResultSync());
}
In UI:
private async void test()
{
string result = await GetResultAsync();
// update UI no problem
textbox.Text = result;
}

Categories