Using a BlockingCollection to queue Tasks - c#

I am trying to create a way to queue up Tasks to run, so I have tried to implement it using a BlockingCollection. The problem I find is whenever I try to add the Task, the Task executes. Sample code as below:
private void button1_Click(object sender, EventArgs e)
{
textBox2.Clear();
for (int i = 0; i < 10; i++)
_processCollection.Add(BigTask(i));
}
static BlockingCollection<Task> _processCollection = new BlockingCollection<Task>();
Thread ConsumerThread = new Thread(LaunchConsumer);
private static async void LaunchConsumer()
{
while (true)
{
var processTask = _processCollection.Take();
await Task.Run(() => processTask);
}
}
async Task BigTask(int i)
{
await Task.Delay(5000);
textBox2.AppendText($"Text{i}\n");
}
What seems to happen in debug is all the tasks seem to run as they are added into the blocking collection. I tried switching the blocking collection to use Action, but that just leads to nothing happening. As below (only changes shown):
private void button1_Click(object sender, EventArgs e)
{
textBox2.Clear();
for (int i = 0; i < 10; i++)
{
int iC = i;
_processCollection.Add(async () => await BigTask(iC));
}
}
static BlockingCollection<Action> _processCollection = new BlockingCollection<Action>();
Thread ConsumerThread = new Thread(LaunchConsumer);
private static async void LaunchConsumer()
{
while (true)
{
var processTask = _processCollection.Take();
await Task.Run(processTask);
}
}
I feel like I have made some small error somewhere, because it feels like this should work. I have tried to find someone doing something similar but have had no luck, which makes me think maybe my concept is flawed so feel free to suggest an alternative.

_processCollection.Add(BigTask(i)); doesn't work because this calls BigTask(i) immediately, and when that is called, the work starts.
You were on the right track by wrapping this in a separate BigTask launcher, but by using Action, you don't provide your LaunchConsumer with any means to track the progress. await Task.Run(processTask) will continue pretty much immediately with the next task. You need to use Func<Task> to avoid that.
The reason you don't see any results is likely unrelated. Now that you manage to launch the task from your newly created thread, the call to textBox2.AppendText is no longer done from the UI thread. That's not supported. Only the UI thread can access UI objects. You can use textBox2.Invoke to pass an action back to the UI thread, and that action can then call AppendText.
Tested working code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
ConsumerThread.Start();
}
private void button1_Click(object sender, EventArgs e)
{
textBox2.Clear();
foreach (var i in Enumerable.Range(0, 10))
_processCollection.Add(() => BigTask(i));
}
static BlockingCollection<Func<Task>> _processCollection = new BlockingCollection<Func<Task>>();
Thread ConsumerThread = new Thread(LaunchConsumer);
private static async void LaunchConsumer()
{
while (true)
{
var processTask = _processCollection.Take();
await Task.Run(processTask);
}
}
async Task BigTask(int i)
{
await Task.Delay(5000);
textBox2.Invoke(new Action(() => textBox2.AppendText($"Text{i}\n")));
}
}
That said, BlockingCollection is not really the best collection type to use here. It dedicates one thread to pretty much nothing but waiting. Also, Task.Run when you're already in a background thread can admittedly sometimes be useful, but doesn't add anything here. What to do instead depends on your needs. Whether all tasks are known beforehand makes a difference. Whether you may want multiple consumers makes a difference. Other things I haven't thought of may also make a difference.

Related

Is there a way to encode a value/result when signaling a WaitHandle or similar class?

As far as I can see AutoResetEvent and ManualEvent simply control a single signal for cross-thread communication.
Often you would want to know some sort of result in the waiting thread and I can only see the option of storing a class member which is accessed by each thread e.g.:
this.WaitHandle = new AutoResetEvent(false);
DoStuff();
WaitHandle.WaitOne();
lock(this._lock)
{
if(this.Result ...){...}
}
void DoStuff()
{
...
lock(this._lock)
{
this.Result = ...;
}
this.WaitHandle.Set();
}
This is a bit cumbersome so I wondered if there is something built in that will let me pass a value when signalling?
The best approach would be to use the async patterns consistently in all your code.
If you want to actually block execution and wait for the result to be available in a blocking way using traditional methods, you can use a BlockingCollection<T> as a channel to both communicate the result of the operation and that the operation is finished at the same time.
private void button1_Click(object sender, EventArgs e)
{
BlockingCollection<string> blockingQueue = new BlockingCollection<string>();
// Start DoStuff on parallel thread
Task.Run(() => DoStuff(blockingQueue));
// Wait for DoRun to finish AND get the result at the same time
string result = blockingQueue.Take();
MessageBox.Show(result);
}
private void DoStuff(BlockingCollection<string> result)
{
// Simulate work
Thread.Sleep(1000);
// return result
result.Add("SomeResultValue");
}
This also allows you to have two threads adding stuff to the queue and retrieving stuff from the queue at the same time in parallel, the consumer always blocking as long as there is no new data available.
private void button1_Click(object sender, EventArgs e)
{
BlockingCollection<string> blockingQueue = new BlockingCollection<string>();
// Start DoStuff on parallel thread
Task.Run(() => DoStuff(blockingQueue));
// Wait for something to be returned from DoStuff and handle it
foreach (string data in blockingQueue.GetConsumingEnumerable())
{
textBox1.AppendText(data + Environment.NewLine);
}
MessageBox.Show("Finished");
}
private void DoStuff(BlockingCollection<string> result)
{
for (int i = 1; i <= 10; i++)
{
// Simulate work
Thread.Sleep(1000);
// return result
result.Add("Result number " + i);
}
// Signal we are done
result.CompleteAdding();
}
If you use a BlockingCollection with BoundedCapacityset to 1, trying to add to the collection would actually wait until the previous value has been removed.

C# WPF - How to load data to datagrid in thread

I have a problem with application freezes for a few seconds.
I loading data from XML file and deserialize to MyList.
public List<My20FieldsDataRecord> MyList;
...
void ShowDataInThread()
{
MyGrid.DataContext = MyList;
}
public void ShowDane(bool inThread)
{
if (inThread)
{
Thread thr = new Thread(ShowDataInThread);
thr.Start();
}
else
{
ShowDataInThread();
}
}
if inThread = false everything work fine, but application not responding for a 2-3 seconds.
When inThread = true application crash.
I want do this in thread, but i was not able to understand that how it works from examples on internet. I'll be very grateful for your help, becouse i have no idea how to do that.
Since Microsoft introduced the async / wait approach for .NET Framework programming in .NET 4.5, the code for async methods is a lot.
You can not find any async async with as example as such as type:
private async void button1_Click(object sender, EventArgs e)
{
string result = await AnMethodAsync();
textBox1.Text += result;
}
private Task<string> AnMethodAsync()
{
//Do somethine async
}
And you think this is done, the function will run async do not have to worry about hanging thead anymore, too strong.
But the problem is not so simple.
Now try to put in the AnMethodAsync () function the following code:
Thread.Sleep(5000);
return Task.FromResult("HoanHT");
Run the code above and when you press button1, the UI will hang stiff for 5s.
What the hell, I have applied async / await properly that the UI is still hanging.
After a brief look at the problem: In the AnMethodAsync function does not create any other task on the other thread. The consequence is that asyn away, but it still runs on UI thread -> UI freeze.
Then fix it, there are two ways:
Method 1: Create a new task and executable:
return Task.Factory.StartNew(() =>
{
Thread.Sleep(5000);
return "HoanHT";
});
Method 2: Use Task.Delay () instead of Thread.Sleep ()
private async Task<string> AnMethodAsync()
{
await Task.Delay(5000);
return "HoanHT";
}
This is the way I usually do with the asynchronous problem, plus one more way is to use ConfigureAwait () for the Task but will cause minor problems.
Here is a way that I've found to load data for a datagrid in the background while not blocking your UI.
First, create a lock object, and enable collection synchronization, then actually load the data on a background thread using Task.Run():
private readonly object _myDataLock = new object();
private FastObservableCollection<My20FieldsDataRecord> MyList = new FastObservableCollection<My20FieldsDataRecord>();
private CollectionViewSource MyListCollectionView = new CollectionViewSource();
public MyViewModelConstructor() : base()
{
// Other ctor code
// ...
// assign the data source of the collection views
MyListCollectionView.Source = MyList;
// Setup synchronization
BindingOperations.EnableCollectionSynchronization(MyList, _myDataLock);
}
private async void LoadMyList()
{
// load the list
await Task.Run(async () =>
{
MyList.ReplaceAll(await MyRepository.LoadMyList());
}
);
}
Then in your repository you could write:
public virtual async Task<IList<My20FieldsDataRecord>> LoadMyList()
{
var results = await this.DataContext.TwentyFieldDataRecords
.OrderByDescending(x => x.MyDate).ToListAsync().ConfigureAwait(false);
return results;
}
Then you could bind in your associated view like this:
<controls:DataGrid Name="MyListDataGrid" Grid.Row="1"
....
ItemsSource="{Binding MyListCollectionView.View}"
... >
For details, please see:
https://blog.stephencleary.com/2014/04/a-tour-of-task-part-0-overview.html
http://blog.stephencleary.com/2012/02/async-and-await.html#avoiding-context
https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(System.Windows.Data.BindingOperations.EnableCollectionSynchronization);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.7);k(DevLang-csharp)&rd=true
https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application

Dispatcher.BeginInvoke() not running asynchronously

Here's a simplified version of what I want to do:
onClick a button, a aNewMethod() would run asynchronously to keep UI responsive. That's it!
I've read some answers and here's what i could come up with :
private async void button_Click(object sender, RoutedEventArgs e)
{
Task task = Task.Run(() => aNewMethod());
await task;
}
private void aNewMethod()
{
if (progress.Value == 0)
{
//Heavy work
for (int i = 1; i < 1000000000; i++) { }
progress.Value = 100;
}
}
As you may have thought, this throws a System.InvalidOperationException at if(progress.Value== 0) saying :
The calling thread cannot access this object because a different
thread owns it.
after some Googling, I've read that I need a Dispatcher.BeginInvoke() method to update/use UI controls, so I did this :
private void aNewMethod()
{
Dispatcher.BeginInvoke((Action)delegate {
if (progress.Value == 0)
{
//Heavy work
for (int i = 1; i < 1000000000; i++) { }
progress.Value = 100;
}
});
}
This solved the System.InvalidOperationException but it's not running asynchronously as the UI still freezes at for loop
So the question is : How to run the aNewMethod(); asynchronously and still update and interact with UI controls ?
The Dispatcher runs in the UI thread. It handles your UI, and executes actions you pass to it via BeginInvoke etc.
But it can only handle one thing at a time; when it's busy handling your action, it won't update the UI in the meantime, so the UI freezes in the meantime.
If you want to keep your UI responsive, you'd need to run the heavy load functions in a seperate, non-UI thread. Within those functions running on another thread, you can call the dispatcher whenever you need access to the UI - ideally, only very briefly for the purpose of updating UI elements.
So in other words, you'd want to be running your sleep function in a seperate thread, and then just make a call to the Dispatcher from your own thread when you need to set the progress value. Something like
private void button_Click(object sender, RoutedEventArgs e)
{
Task task = Task.Run(() => aNewMethod()); // will call aNewMethod on the thread pool
}
private void aNewMethod()
{
double progressValue = 0;
Dispatcher.Invoke(() => progressValue = progress.Value);
if (progressValue == 0)
{
Thread.Sleep(3000); // still executes on the threadpool (see above), so not blocking UI
Dispatcher.Invoke(() => progress.Value = 100 ); // call the dispatcher to access UI
}
}
With you current implementation, there is no need to use Thread.Start, as in that method you are just sleeping it for some time and accessing UI thread objects there which is not allowed
.In your scenario a better way of doing is that you should not use Thread.Sleep instead of that you should be doing it with Task.Delay like:
private async void button_Click(object sender, RoutedEventArgs e)
{
if (progress.Value == 0)
{
await Task.Delay(3000);
progress.Value = 100;
}
}
Now you don't need Dispatcher.Invoke, and credit goes to async and await keywords as statements after await will be executing in the same calling synchronization context from where we did async call which is UI thread in this case.
onClick a button, a aNewMethod() would run asynchronously to keep UI responsive.
Actually, it's running synchronously on a background thread. Task.Run is perfectly appropriate for this.
after some Googling, I've read that I need a Dispatcher.BeginInvoke() method to update/use UI controls
Unfortunately, this is one area where Google will certainly mislead you. Dispatcher is not the best solution here.
Instead, you should use IProgress<T>/Progress<T>, as such:
private async void button_Click(object sender, RoutedEventArgs e)
{
var progress = new Progress<int>(value => { this.progress.Value = value; });
await Task.Run(() => aNewMethod(progress));
}
private void aNewMethod(IProgress<int> progress)
{
//Heavy work
for (int i = 1; i < 1000000000; i++) { }
progress.Report(100);
}

Update UI control in Task

Does it right code, if I update my UI controls in task like this?
Or it's wrong, and I need use smth like Control.Invoke?
private async void button1_Click(object sender, EventArgs e)
{
textBox1.Text = await Task<string>.Factory.StartNew(() =>
{
Foo();
return "Completed";
});
}
private void Foo()
{
for (int i = 0; i < 100; i++)
{
textBox1.Text = i.ToString();
Thread.Sleep(1000);
}
}
The BCL addresses this specific scenario with the IProgress interface, implemented in the Progress class, to provide rich asynchronous progress reporting. This is available in .NET 4.5, or .NET 4 with the BCL Portability Nuget package. A lot of BCL classes accept an IProgress parameter for progress reporting.
Servy's answer addresses the immediate problem of how to update the UI after an asynchronous operation, but that forces you to mix UI code inside the long running operation. IProgress allows you to make an OnReport call with your report data without any concerns about marshalling the call to the proper thread, synchronization contexts, UI specific calls etc.
Your code can be as simple as this:
private async void button1_Click(object sender, EventArgs e)
{
var progress=new Progress<string>(msg=>textBox1.Text = msg);
await Task<string>.Factory.StartNew(() => Foo(progress));
}
private void Foo(IProgress<string> progress)
{
for (int i = 0; i < 100; i++)
{
progress.OnReport(i.ToString());
Thread.Sleep(1000);
}
progress.OnReport("Finished");
}
Or you can use a more complex progress type, eg
class MyProgressData
{
public string Message{get;set;}
public int Iteration {get;set;}
public MyProgressData(string message,int iteration) ...
}
private async void button1_Click(object sender, EventArgs e)
{
var progress=new Progress<MyProgressData>(msg=>{
textBox1.Text = msg.Message;
textBox2.Text=msg.Iteration.ToString();
});
await Task<string>.Factory.StartNew(() => Foo(progress));
}
private void Foo(IProgress<MyProgressData> progress)
{
for (int i = 0; i < 100; i++)
{
progress.OnReport(new MyProgressData("Hi",i));
Thread.Sleep(1000);
}
progress.OnReport("Finished");
}
The beauty of this is that you can completely decouple processing from reporting. You can put the processing code in a completely different class or even project from your UI.
You can just run the code to see that it won't work, and that your program will crash, as you are touching the UI from a non-UI thread.
You should instead update the UI as a continuation to the task that is scheduled to run in the UI thread. await does all of this for you, making the code pretty trivial.
You also shouldn't create a thread pool thread just to have it sit there and do nothing while you sleep for a fixed amount of time. Use Task.Delay instead.
private async void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 100; i++)
{
textBox1.Text = i.ToString();
await Task.Delay(1000);
}
textBox1.Text = "Completed";
}
Your Foo method will throw, since you're trying to update a UI control from a background thread.
If Foo did something arbitrary, which doesn't evolve any UI controls, then your textBox1.Text = await Task.Factory.StartNew would be fine, since you use await and the SynchronizationContext would be implicitly captured and used once the await completes, which would make the assignment on the UI thread.
This doesn't make too much sense, but if you wanted to update a control from inside the Task, you'd have to use Control.Invoke.

Why Task Parallel Library can update UI simply?

When I try update UI using Task in .net Framework 4, I found out that something strange. I never thought UI thread can be updated from Task Library. I just wanted to test it and amazingly it works. Here is my code, can someone explain how it works ?
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
int i=0;
Task myTask = new Task(() =>
{
while (true)
{
label1.Text = "Hello" + i++;
Thread.Sleep(3000);
};
});
myTask.Start();
}
The fact that you didn't get an exception this time doesn't mean that you (or even worse your customer) won't get an exception the next time. You were just lucky. Make sure you marshal all function calls to the UI on the main thread. Or if you want to spare this task use a BackgroundWorker which will take care of executing the callback on the main thread.

Categories