Home > Blockchain >  Creating a new Thread in each iteration of foreach loop
Creating a new Thread in each iteration of foreach loop

Time:02-02

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!");
    }
  • Related