简体   繁体   English

只有当可选的主线程任务和工作线程完成时,我如何保证代码的执行?

[英]How do I guarantee execution of code only if and when optional main thread task and worker threads are finished?

Background:背景:

I have an application I am developing that deals with a large number of addons for another application.我正在开发一个应用程序,它处理另一个应用程序的大量插件。 One if its primary uses is to safely modify file records in files with fewer records so that they may be treated as one file (almost as if it is combing the files together into one set of records. To do this safely it keeps track of vital information about those files and changes made to them so that those changes can be undone if they don't work as expected.一个如果它的主要用途是安全地修改具有较少记录的文件中的文件记录,以便它们可以被视为一个文件(几乎就像它将文件组合成一组记录一样。为了安全地做到这一点,它会跟踪重要的有关这些文件和对它们所做的更改的信息,以便在它们没有按预期工作时可以撤消这些更改。

When my application starts, it analyzes those files and keeps essential properties in a cache (to reduce load times).当我的应用程序启动时,它会分析这些文件并将基本属性保存在缓存中(以减少加载时间)。 If a file is missing from the cache, the most important stuff is retrieved and then a background worker must process the file for more information.如果缓存中缺少文件,则检索最重要的内容,然后后台工作人员必须处理该文件以获取更多信息。 If a file that was previously modified has been updated with a new version of the file, the UI must confirm this with the user and its modification data removed.如果之前修改过的文件已更新为文件的新版本,则 UI 必须与用户确认这一点并删除其修改数据。 All of this information, including information on its modification is stored in the cache.所有这些信息,包括有关其修改的信息,都存储在缓存中。

My Problem:我的问题:

My problem is that neither of these processes are guaranteed to run (the confirmation window or the background file processor).我的问题是这些进程都不能保证运行(确认 window 或后台文件处理器)。 If either of them run, then the cache must be updated by the main thread.如果其中任何一个运行,则缓存必须由主线程更新。 I don't know enough about worker threads, and which thread runs the BackgroundWorker.RunWorkerCompleted event handler in order to effectively decide how to approach guaranteeing that the cache updater is run after either (or both) processes are completed.我对工作线程以及哪个线程运行 BackgroundWorker.RunWorkerCompleted 事件处理程序了解不够,以便有效地决定如何确保缓存更新程序在一个(或两个)进程完成后运行。

To sum up: if either process is run, they both must finish and (potentially) wait for the other to be completed before running the cache update code.总结一下:如果任一进程运行,它们都必须完成并(可能)等待另一个进程完成,然后再运行缓存更新代码。 How can I do this?我怎样才能做到这一点?

ADJUNCT INFO (My current intervention that doesn't seem to work very well):附加信息(我目前的干预似乎效果不佳):

I have a line in the RunWorkerCompleted handler that waits until the form reference is null before continuing and exiting but maybe this was a mistake as it sometimes locks my program up.我在 RunWorkerCompleted 处理程序中有一行,它一直等到表单引用为 null 后再继续并退出,但这可能是一个错误,因为它有时会锁定我的程序。

SpinWait.SpinUntil(() => overwriteForm == null);

I haven't included any more code because I anticipate that this is more of a conceptual question than a code one.我没有包含更多代码,因为我预计这更像是一个概念问题而不是代码问题。 However, if necessary, I can supply code if it helps.但是,如有必要,如果有帮助,我可以提供代码。

I think CountDownTask is what you need我认为CountDownTask是你需要的

using System;
using System.Threading;

public class Program
{

    public class AtomicInteger
    {
        protected int value = 0;

        public AtomicInteger(int value)
        {
            this.value = value;
        }

        public int DecrementAndGet()
        {
            int answer = Interlocked.Decrement(ref value);
            return answer;
        }
    }

    public interface Runnable
    {
        void Run();
    }

    public class CountDownTask
    {
        private AtomicInteger count;
        private Runnable task;
        private Object lk = new Object();
        private volatile bool runnable;
        private bool cancelled;

        public CountDownTask(Int32 count, Runnable task)
        {
            this.count = new AtomicInteger(count);
            this.task = task;
            this.runnable = false;
            this.cancelled = false;
        }

        public void CountDown()
        {
            if (count.DecrementAndGet() == 0)
            {
                lock (lk)
                {
                    runnable = true;
                    Monitor.Pulse(lk);
                }
            }
        }

        public void Await()
        {
            lock (lk)
            {
                while (!runnable)
                {
                    Monitor.Wait(lk);
                }
                if (cancelled)
                {
                    Console.WriteLine("Sorry! I was cancelled");
                }
                else {
                    task.Run();
                }
            }
        }

        public void Cancel()
        {
            lock (lk)
            {
                runnable = true;
                cancelled = true;
                Monitor.Pulse(lk);
            }
        }
    }

    public class HelloWorldTask : Runnable
    {
        public void Run()
        {
            Console.WriteLine("Hello World, I'm last one");
        }
    }

    public static void Main()
    {
        Thread.CurrentThread.Name = "Main";
        Console.WriteLine("Current Thread: " + Thread.CurrentThread.Name);
        CountDownTask countDownTask = new CountDownTask(3, new HelloWorldTask());
        Thread worker1 = new Thread(() => {
            Console.WriteLine("Worker 1 run");
            countDownTask.CountDown();
        });
        Thread worker2 = new Thread(() => {
            Console.WriteLine("Worker 2 run");
            countDownTask.CountDown();
        });
        Thread lastThread = new Thread(() => countDownTask.Await());
        lastThread.Start();
        worker1.Start();
        worker2.Start();
        //countDownTask.Cancel();
        Console.WriteLine("Main Thread Run");
        countDownTask.CountDown();
        Thread.Sleep(1000);
    }
}

let me explain (but you can refer Java CountDownLatch )让我解释一下(但你可以参考Java CountDownLatch

1. To ensure a task must run after another tasks, we need create a Wait function to wait for they done, so I used 1.为了确保一个任务必须在另一个任务之后运行,我们需要创建一个Wait function来等待它们完成,所以我使用了
if(count.decrementAndGet() == 0) {
    lock(lk) {
        runnable = true;
        Monitor.Pulse(lk);
    }
}
2. When there is a task done, we need count down, and if count down to zero (it means all of the tasks was done) we will need notify to blocked thread to wake up and process task 2.当有任务完成时,我们需要倒计时,如果倒计时到零(这意味着所有的任务都完成了)我们需要通知阻塞线程唤醒并处理任务
if(count.decrementAndGet() == 0) { lock(lk) { runnable = true; Monitor.Pulse(lk); } }

Let read more about volatile , thanks让我们阅读更多关于volatile的信息,谢谢

While dung ta van's "CountDownTask" answer isn't quite what I needed, it heavily inspired the solution below (see it for more info).虽然 dung ta van 的“CountDownTask”答案并不是我所需要的,但它极大地启发了下面的解决方案(有关更多信息,请参阅它)。 Basically all I did was add some extra functionality and most importantly: made it so that each task "vote" on the outcome (true or false).基本上我所做的只是添加了一些额外的功能,最重要的是:让每个任务对结果“投票”(真或假)。 Thanks dung ta van!谢谢粪大面包车!

To be fair, dung ta van's solution DOES work to guarantee execution which as it turns out isn't quite what I needed.公平地说,dung ta van 的解决方案确实可以保证执行,但事实证明这并不是我所需要的。 My solution adds the ability to make that execution conditional.我的解决方案增加了使执行有条件的能力。

This was my solution which worked:这是我的解决方案:

public enum PendingBool
{
    Unknown = -1,
    False,
    True
}
public interface IRunnableTask
{
    void Run();
}

public class AtomicInteger
{
    int integer;
    public int Value { get { return integer; } }
    public AtomicInteger(int value) { integer = value; }
    public int Decrement() { return Interlocked.Decrement(ref integer); }
    public static implicit operator int(AtomicInteger ai) { return ai.integer; }
}

public class TaskElectionEventArgs
{
    public bool VoteResult { get; private set; }
    public TaskElectionEventArgs(bool vote) { VoteResult = vote; }
}

public delegate void VoteEventHandler(object sender, TaskElectionEventArgs e); 

public class SingleVoteTask
{
    private AtomicInteger votesLeft;
    private IRunnableTask task;
    private volatile bool runTask = false;
    private object _lock = new object();

    public event VoteEventHandler VoteCast;
    public event VoteEventHandler TaskCompleted;

    public bool IsWaiting { get { return votesLeft.Value > 0; } }
    
    public PendingBool Result 
    {
        get
        {
            if (votesLeft > 0)
                return PendingBool.Unknown;
            else if (runTask)
                return PendingBool.True;
            else
                return PendingBool.False;
        }
    }

    public SingleVoteTask(int numberOfVotes, IRunnableTask taskToRun) 
    {
        votesLeft = new AtomicInteger(numberOfVotes);
        task = taskToRun; 
    }

    public void CastVote(bool vote)
    {
        votesLeft.Decrement();
        runTask |= vote;
        VoteCast?.Invoke(this, new TaskElectionEventArgs(vote));
        if (votesLeft == 0)
            lock (_lock)
            {
                Monitor.Pulse(_lock);
            }
    }

    public void Await()
    {
        lock(_lock)
        {
            while (votesLeft > 0)
                Monitor.Wait(_lock);
            if (runTask)
                task.Run();
            TaskCompleted?.Invoke(this, new TaskElectionEventArgs(runTask));
        }
    }
}

Implementing the above solution was as simple as creating the SingleVoteTask in the UI thread and then having each thread affecting the outcome cast a vote.实现上述解决方案就像在 UI 线程中创建 SingleVoteTask 一样简单,然后让每个影响结果的线程投票。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何检测主线程何时完成执行 - How do I detect when the main thread has finished execution 如何在单个catch块中处理工作线程和主线程的异常? - How do I handle exceptions from worker threads and main thread in a single catch block? 我如何只产生一定数量的线程或任务,然后等待,并在每个线程或任务完成时在C#中产生一个新的线程或任务? - How do I spawn only a certain number of threads or tasks and then wait and as each thread or task completes, spawn a new thread or task in C#? 线程完成后如何更新main {}? - How to update main{} when threads are finished? 子线程完成后主线程不会继续 - Main thread not proceeding when child threads have finished 对话框如何在不阻塞主线程的情况下阻止代码执行? - How do dialogs prevent code execution without blocking the main thread? 使用基于事件的异步模式时,如何让主线程等待其他线程? - How do I have the main thread wait for other threads when using the Event-based Asynchronous Pattern? 如何在 C# 的主线程中生成工作线程并处理它们的输出? - How to spawn worker threads and process their outputs in main thread in C#? 我如何有效管理在主线程中运行的准“任务” - How do a I efficiently manage a quasi 'task' running in the main thread 如何将此C#工作线程代码与主线程中的共享数据变量分离? - How can I decouple this C# worker thread code from the main thread for shared data variables?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM