I detect faces in image files inside a folder using Emgu CV. I'm using a foreach
loop for this. However, the form does not respond until the whole process is finished.
I use threads to avoid this. However, the thread is not working as I expected. The loop ends before the rendering of the images is finished.
foreach (var item in files)
{
Img = Image.FromFile(item);
string savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file));
Thread th = new Thread(() => ModernUI.FaceDetect.imageprocessmulti(Img, savefile, savepath));
th.Start();
}
Even if I do it this way, it works like it was before using threads.
th.Start();
th.Join();
CodePudding user response:
The simplest thing is to change from Thread
to Task
(and correctly dispose of your image) like this:
foreach (var item in files)
{
using (Image img = Image.FromFile(item))
{
string savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file));
await Task.Run(() => ModernUI.FaceDetect.imageprocessmulti(img, savefile, savepath));
}
}
That MUST be done in a async Task
method (or async void
event handler) to allow the use of the await
keyword.
This approach will run each imageprocessmulti
one after the other without blocking the UI.
If you want to run all of the imageprocessmulti
in parallel, then you're best off making a list of tasks and awaiting them all at once. Like this:
List<Task> tasks =
(
from item in files
let savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file))
select Task.Run(() =>
{
using (Image img = Image.FromFile(item))
{
ModernUI.FaceDetect.imageprocessmulti(img, savefile, savepath);
}
})
).ToList();
await Task.WhenAll(tasks);
My preferred approach is to use Microsoft's Reactive Framework - NuGet System.Reactive
- and then do this:
IObservable<Unit> query =
from item in files.ToObservable(Scheduler.Default)
let savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file))
from unit in Observable.Using(
() => Image.FromFile(item),
img => Observable.Start(() => ModernUI.FaceDetect.imageprocessmulti(img, savefile, savepath)))
select unit;
await query.ToArray();
All of these approaches should work for you.
CodePudding user response:
Here's one way to approach it using async/await
, for example, with a button click event:
private async void button1_Click(object sender, EventArgs e)
{
await Task.Run(() => {
foreach (var item in files)
{
Img = Image.FromFile(item);
string savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file));
ModernUI.FaceDetect.imageprocessmulti(Img, savefile, savepath);
}
});
// ...more code here to run after all images have been processed...
MessageBox.Show("Done!");
}