I have async method OnValueChange where i call static method as a task, my method is updating gui with help of Iprogress intereface and my method is returning Image:
public async void OnValueChange(object sender, EventArgs e)
{
var progress = new Progress<int>(i => ProgresBar.Value = i);
Image im = await Task.Factory.StartNew(() => MyStaticClass.MyStaticFunction(progress),
TaskCreationOptions.LongRunning);
Picture = im;
}
In some cases i am calling OnValueChange function frequently in short period of time, but i want only one task running at time.
what is most efficient method to check if task with specific method is already running?
You should avoid async void. On a side note, I explain on my blog that the way you're using StartNew is dangerous (you should use Task.Run instead).
Applying both of these guidelines to your example, the code will look like this:
public async Task OnValueChangeAsync()
{
var progress = new Progress<int>(i => ProgresBar.Value = i);
Image im = await Task.Run(() => MyStaticClass.MyStaticFunction(progress));
Picture = im;
}
public async void OnValueChange(object sender, EventArgs e)
{
await OnValueChangeAsync();
}
At this point, it becomes more clear how to detect whether a method is already running (since that method actually returns a Task now). One implementation is as such:
private async Task OnValueChangeImplAsync()
{
var progress = new Progress<int>(i => ProgresBar.Value = i);
Image im = await Task.Run(() => MyStaticClass.MyStaticFunction(progress));
Picture = im;
_onValueChangeTask = null;
}
private Task _onValueChangeTask;
public Task OnValueChangeAsync()
{
if (_onValueChangeTask != null)
return _onValueChangeTask;
_onValueChangeTask = OnValueChangeImplAsync();
return _onValueChangeTask;
}
public async void OnValueChange(object sender, EventArgs e)
{
await OnValueChangeAsync();
}
Related
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);
}
I know that for async operations it is possible to track its progress, but I will try that later. Now I have a simple window forms apply with a button (or a pair of buttons - the number does not matter). The buttons call an async operation
public async void Button1_Click(...)
{
await Button1_OperationAsync(...);
}
If I don't press the button nothing is going on but if I press it once the Button1_OperationAsync starts (and is awaited). (I am not really sure if to call it "a thread").
But what happens if I press the button twice? Well then before the first async operation finishes, the Button1_OperationAsync is called again. (Or if another similar button is pressed then a Button2_OperationAsync is called)
Maybe even the second async operation would finish before the first one.
What I want is a simple way of knowing if any operation is going on. So what I thought is to have a variable and increment it when an operation is called and decrement it when an operation is finished. Something like
int numberOfOps=0;
public async void Button1_Click(...)
{ numberOfOps++;
textBox1.Text="Started!";
await Button1_OpeationAsync(...);
numberOfOps--;
if(numberOfOps<=0)
{
textBox1.Text="Done!";
}
}
Be aware that this code would go in the other button (Button2) too. Or many other buttons.
I am aware that issues of synchronization might be involved. So I would appreciate advice on what I am trying to do in order to do correctly
When using async/await you're not using any threads for the UI code other than the UI-thread. It's possible that the code that gets called in the Button1_OpeationAsync method might use a separate thread, but the calling code will remain on the UI thread.
Try having a play with this code:
private int numberOfOps = 0;
private async void button1_Click(object sender, EventArgs e)
{
textBox1.Text = $"Started! {++numberOfOps}";
await Task.Delay(TimeSpan.FromSeconds(5.0));
textBox1.Text = $"Started! {--numberOfOps}";
if (numberOfOps == 0)
{
textBox1.Text = "Done!";
}
}
It works just fine. You can use the numberOfOps variable across multiple buttons.
If you'd like to make it easy to re-use the code, try it this way:
int numberOfOps = 0;
private async Task RunOp(Func<Task> op)
{
textBox1.Text = $"Started! {++numberOfOps}";
await op();
textBox1.Text = $"Started! {--numberOfOps}";
if (numberOfOps == 0)
{
textBox1.Text = "Done!";
}
}
private async void button1_Click(object sender, EventArgs e)
{
await this.RunOp(() => Button1_OpeationAsync(...));
}
private async void button2_Click(object sender, EventArgs e)
{
await this.RunOp(() => Button2_OpeationAsync(...));
}
Have a task array, and a task object at class level:
private List<Task> tasks = new List<Task>();
private Task task = null;
In each of your click handlers do something like this:
var operationTask = SomeOperationAsync(...);
tasks.Add(operationTask);
task = Task.WhenAll(tasks);
if (task.IsCompleted)
{
// no operation is going on
tasks.Clear();
// do what ever you want to do further
}
else
{
//some operation is going on
}
I need to populate a column in a DataGridView with a thumbnail image. I would like to load the DataGridViewImageCell.Value asynchronously as it does take some time to download the images.
This solution loads the images asynchronously, but it appears to prevent the UI thread from performing other tasks (I assume because the application's message queue is filled with the .BeginInvoke calls).
How can this be accomplished yet still allow the user to scroll through the grid while images are being downloaded?
private void LoadButton_Click(object sender, EventArgs e)
{
myDataGrid.Rows.Clear();
// populate with sample data...
for (int index = 0; index < 200; ++index)
{
var itemId = r.Next(1, 1000);
var row = new DataGridViewRow();
// itemId column
row.Cells.Add(new DataGridViewTextBoxCell
{
ValueType = typeof(int),
Value = itemId
});
// pix column
row.Cells.Add(new DataGridViewImageCell
{
ValueType = typeof(Image),
ValueIsIcon = false
});
// pre-size height for 90x120 Thumbnails
row.Height = 121;
myDataGrid.Rows.Add(row);
// Must be a "better" way to do this...
GetThumbnailForRow(index, itemId).ContinueWith((i) => SetImage(i.Result));
}
}
private async Task<ImageResult> GetThumbnailForRow(int rowIndex, int itemId)
{
// in the 'real world' I would expect 20% cache hits.
// the rest of the images are unique and will need to be downloaded
// emulate cache retrieval and/or file download
await Task.Delay(500 + r.Next(0, 1500));
// return an ImageResult with rowIndex and image
return new ImageResult
{
RowIndex = rowIndex,
Image = Image.FromFile("SampleImage.jpg")
};
}
private void SetImage(ImageResult imageResult)
{
// this is always true when called by the ContinueWith's action
if (myDataGrid.InvokeRequired)
{
myDataGrid.BeginInvoke(new Action<ImageResult>(SetImage), imageResult);
return;
}
myDataGrid.Rows[imageResult.RowIndex].Cells[1].Value = imageResult.Image;
}
private class ImageResult
{
public int RowIndex { get; set; }
public Image Image { get; set; }
}
Methods like ContinueWith() are since the introduction of async-await fairly out-of-date. Consider using real async-await
Every now and then your thread has to wait for something, wait for a file to be written, wait for a database to return information, wait for information from a web site. This is a waste of computing time.
Instead of waiting, the thread could look around to see it if could do something else, and return later to continue with the statements after the wait.
Your function GetThumbNail for row simulates such a wait in the Task.Delay. Instead of waiting, the thread goes up it's call stack to see it its caller is not awaiting for the result.
You forgot to declare your LoadButton_Click async. Therefore your UI isn't responsive.
To keep a UI responsive while an event handler is busy, you have to declare your event handler async and use awaitable (async) functions whenever possible.
Keep in mind:
a function with a await should be declared async
every async function returns Task instead of void and Task<TResult> instead of TResult
The only exception to this is the event handler. Although it is declared async, it returns void.
if you await a Task the return is void; if you await a Task<TResult> the return is TResult
So your code:
private async void LoadButton_Click(object sender, EventArgs e)
{
...
// populate with sample data...
for (int index = 0; index < 200; ++index)
{
...
ImageResult result = await GetThumbnailForRow(...);
}
}
private async Task<ImageResult> GetThumbnailForRow(int rowIndex, int itemId)
{
...
await Task.Delay(TimeSpan.FromSeconds(2));
return ...;
}
Now whenever the await in your GetThumbnailForRow is met, the thread goes up its call stack to see if the caller is not awaiting the result. In your example the caller is awaiting, so it goes up its stack to see... etc. Result: whenever your thread isn't doing anything your user interface is free to do other things.
However you could improve your code.
Consider to start loading the thumbnail as at the beginning or your event handler. You don't need the result immediately and there are other useful things to do. So don't await for the result, but do those other things. Once you need the result start awaiting.
private async void LoadButton_Click(object sender, EventArgs e)
{
for (int index = 0; index < 200; ++index)
{
// start getting the thumnail
// as you don't need it yet, don't await
var taskGetThumbNail = GetThumbnailForRow(...);
// since you're not awaiting this statement will be done as soon as
// the thumbnail task starts awaiting
// you have something to do, you can continue initializing the data
var row = new DataGridViewRow();
row.Cells.Add(new DataGridViewTextBoxCell
{
ValueType = typeof(int),
Value = itemId
});
// etc.
// after a while you need the thumbnail, await for the task
ImageResult thumbnail = await taskGetThumbNail;
ProcessThumbNail(thumbNail);
}
}
If getting thumbnails is independently waiting for different sources, like waiting for a web site and a file, consider starting both functions and await for them both to finish:
private async Task<ImageResult> GetThumbnailForRow(...)
{
var taskImageFromWeb = DownloadFromWebAsync(...);
// you don't need the result right now
var taskImageFromFile = GetFromFileAsync(...);
DoSomethingElse();
// now you need the images, start for both tasks to end:
await Task.WhenAll(new Task[] {taskImageFromWeb, taskImageFromFile});
var imageFromWeb = taskImageFromWeb.Result;
var imageFromFile = taskImageFromFile.Result;
ImageResult imageResult = ConvertToThumbNail(imageFromWeb, imageFromFile);
return imageResult;
}
Or you could start getting all thumbnails without await and await for all to finish:
List<Task<ImageResult>> imageResultTasks = new List<Task<ImageResult>>();
for (int imageIndex = 0; imageIndex < ..)
{
imageResultTasks.Add(GetThumbnailForRow(...);
}
// await for all to finish:
await Task.WhenAll(imageResultTasks);
IEnumerable<ImageResult> imageResults = imageResulttasks
.Select(imageResultTask => imageResultTask.Result);
foreach (var imageResult in imageResults)
{
ProcesImageResult(imageResult);
}
If you have to do some heavy calculations, without waiting for something, consider creating an awaitable async function to do this heavy calculation and let a separate thread do these calculations.
Example: the function to convert the two images could have the following async counterpart:
private Task<ImageResult> ConvertToThumbNailAsync(Image fromWeb, Image fromFile)
{
return await Task.Run( () => ConvertToThumbNail(fromWeb, fromFile);
}
An article that helped me a lot, was Async and Await by Stephen Cleary
The analogy to prepare a meal described in this interview with Eric Lippert helped me to understand what happens when your thread encounters an await. Search somewhere in the middle for async-await
Start by making your event handler async:
private async void LoadButton_Click(object sender, EventArgs e)
Then change this line:
GetThumbnailForRow(index, itemId).ContinueWith((i) => SetImage(i.Result));
to:
var image = await GetThumbnailForRow(index, itemId);
SetImage(image);
there have been some time since i worked with tasks and lambda expressions. Is this a good way to run a anonymous task with a lambda expression and then run code on the UI thread when task is finished?
private void btn_mods_Click(object sender, RoutedEventArgs e)
{
function_buttons_stackpanel.IsEnabled = false;
Loading();
Task task = new Task(() => {
if (IsServiceIsUp() != false)
{
webServiceMods = JsonConvert.DeserializeObject(_webServiceResponse).mods;
webServiceBaseUrl = JsonConvert.DeserializeObject(_webServiceResponse).basePath;
Console.Write(webServiceBaseUrl);
}
});
task.Start();
task.ContinueWith((foo) =>
{
FinishedLoading();
function_buttons_stackpanel.IsEnabled = true;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
private void Loading()
{
img_loading.Visibility = Visibility.Visible;
}
private void FinishedLoading()
{
img_loading.Visibility = Visibility.Collapsed;
}
I tried to chain the task.Start directly but that gave me an error Cannot Implicitly convert type void to System.Threading.Tasks.Task.
Basically what i wanted to do was to chain the whole process from start to end.
Task task = new Task(() => {
if (IsServiceIsUp() != false)
{
webServiceMods = JsonConvert.DeserializeObject(_webServiceResponse).mods;
webServiceBaseUrl = JsonConvert.DeserializeObject(_webServiceResponse).basePath;
Console.Write(webServiceBaseUrl);
}
}).Start();
In PHP I would do something like this:
$task = new Task(() => {
if (IsServiceIsUp() != false)
{
$webServiceMods = JsonConvert::DeserializeObject($_webServiceResponse).mods;
$webServiceBaseUrl = JsonConvert::DeserializeObject($_webServiceResponse).basePath;
Console::Write($webServiceBaseUrl);
}
})
->Start()
->ContinueWith(($foo) =>
{
FinishedLoading();
$function_buttons_stackpanel.IsEnabled = true;
}, TaskScheduler::FromCurrentSynchronizationContext());
Is this possible? If so, is there any reason to not do it, and if there is a better way to do this, could you give me an example?
And thanks!
You can do this rather easily and a bit cleaner with async-await:
private async void btn_mods_Click(object sender, RoutedEventArgs e)
{
if (!IsServiceIsUp())
return;
function_buttons_stackpanel.IsEnabled = false;
Loading();
await Task.Run(() =>
{
var result = JsonConvert.DeserializeObject(_webServiceResponse);
Console.Write(result.webServiceBaseUrl);
});
FinishedLoading();
function_buttons_stackpanel.IsEnabled = true;
}
Performance wise, I wouldn't be so sure you'd need to use a threadpool thread just for deserializing a JSON. I would definitely test this code to determine if it's worth it.
If you declare your event handler as async function, then you don't have to start the task, it already runs asynchronously.
All async functions should return Task instead of void and Task<TResult> instead of TResult. The only exception is the event handler. The event handler returns void
In proper async-await this would be as follows:
private async void btn_mods_Click(object sender, RoutedEventArgs e)
{
function_buttons_stackpanel.IsEnabled = false;
Loading();
if (IsServiceIsUp())
{ // The service is up, start the deserialization
// because this function is async, you can call other async functions
// without having to start a task
// The UI remains responsive
webServiceMods = await JsonConvert.DeserializeObjectAsync(_webServiceResponse).mods;
// because of the await above, the second task is started after the first is finished:
webServiceBaseUrl = await JsonConvert.DeserializeObjectAsync(_webServiceResponse).basePath;
// because of the await the second task is also finished
Console.Write(webServiceBaseUrl);
FinishedLoading();
function_buttons_stackpanel.IsEnabled = true;
}
}
By making your event handler async your code will make full usage of async-await. Your code would look much neater without the magic of continueWith and other Task related functions from before the async-await era.
One final remark: the following code looks silly and unnecessary difficult:
if (IsServiceIsUp() != false) ...
This should of course be:
if (IsServiceIsUp()) ...
The following Code does not change the Text and stops executing the Task
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "Test";
Task.Run(() => MyAsyncMethod());
}
public async Task MyAsyncMethod()
{
label1.Text = "";
//everything from here on will not be executed
}
would be really handy if you could use async together with the UI
for accessing a GUI control through a second thread you need to invoke.
following example shows how to set a label's text properly
private void setLabel1TextSafe(string txt)
{
if(label1.InvokeRequired)
label1.Invoke(new Action(() => label1.Text = txt));
else
label1.Text = txt;
}
I hope this solves your problem
would be really handy if you could use async together with the UI
The design of async was carefully done so you can use it naturally with the UI.
in my code i run a function that does a lot of IO and stuff that takes a long time
If you have asynchronous I/O methods (which you should), then you can just do this:
private async void button1_Click(object sender, EventArgs e)
{
label1.Text = "Test";
await MyMethodAsync();
}
public async Task MyMethodAsync()
{
label1.Text = "";
await ...; // "lot of IO and stuff"
label1.Text = "Done";
}
That's the most natural approach.
However, if you need to run code on a background thread (e.g., it's actually CPU-bound, or if you just don't want to make your I/O operations asynchronous like they should be), then you can use IProgress<T>:
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "Test";
var progress = new Progress<string>(update => { label1.Text = update; });
await Task.Run(() => MyMethod(progress));
}
public void MyMethod(IProgress<string> progress)
{
if (progress != null)
progress.Report("");
...; // "lot of IO and stuff"
if (progress != null)
progress.Report("Done");
}
Under no circumstances should modern code use Control.Invoke or (even worse) Control.InvokeRequired.
Task.Run is used to envelope an Action (that is not async) into a Task. Any Task that you want to execute should be awaited. Thus, that Task.Run of yours is rigorously doing nothing.
Mark that button1_Click event handler of yours as async. Then remove that Task.Run and instead do await MyAsyncMethod().
Try this. You don't need to fire a new thread to invoke the async method. compiler will do it for you.
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "Test";
MyAsyncMethod();
}
public async Task MyAsyncMethod()
{
return await Task.Run(() =>{
label1.Text = "";
//everything from here on will not be executed
}
}
I think both the questions and some of the answers are not clear. Depending on where in the task thread you need to update the label you have to use invoke. Otherwise you can leverage await and leverage the standard semantics.
private async void button1_Click(object sender, EventArgs e)
{
label1.Text = "Starting to run a long task... carry on...";
await snooze(3);
label1.Text = "Done with long task";
}
public Task<int> snooze(int seconds)
{
label1.Text = "zzzz...";
return Task.Run(
() => {
label1.Invoke(new Action(() => label1.Text = "For some reason I need to alert you here.. bad dream perhaps...direct access to label1 will fail"));
Thread.Sleep(seconds * 1000);
return seconds;
});
}