如何保证只有当可选的主线程任务和工作线程完成时才执行代码?
How do I guarantee execution of code only if and when optional main thread task and worker threads are finished?
背景:
我正在开发一个应用程序,它处理另一个应用程序的大量插件。如果它的主要用途是安全地修改记录较少的文件中的文件记录,以便它们可以被视为一个文件(几乎就像将文件组合成一组记录一样)。为了安全地做到这一点,它会跟踪重要的有关这些文件和对它们所做更改的信息,以便在它们未按预期工作时可以撤消这些更改。
当我的应用程序启动时,它会分析这些文件并将基本属性保存在缓存中(以减少加载时间)。如果缓存中缺少文件,则会检索最重要的内容,然后后台工作人员必须处理该文件以获取更多信息。如果以前修改过的文件已更新为文件的新版本,UI 必须与用户确认这一点并删除其修改数据。所有这些信息,包括有关其修改的信息都存储在缓存中。
我的问题:
我的问题是这两个进程都不能保证运行(确认window 或后台文件处理器)。如果其中任何一个运行,那么缓存必须由主线程更新。我不太了解工作线程,哪个线程 运行 是 BackgroundWorker.RunWorkerCompleted 事件处理程序,以便有效地决定如何保证缓存更新程序在任一(或both) 进程都完成了。
总结一下:如果任一进程是 运行,它们都必须完成并(可能)等待另一个完成,然后才能 运行 缓存更新代码。我该怎么做?
ADJUNCT INFO(我目前的干预似乎效果不是很好):
我在 RunWorkerCompleted 处理程序中有一行等待表单引用为空,然后继续并退出,但这可能是一个错误,因为它有时会锁定我的程序。
SpinWait.SpinUntil(() => overwriteForm == null);
我没有包含更多代码,因为我预计这是一个概念性问题,而不是代码问题。但是,如有必要,如果有帮助,我可以提供代码。
我觉得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);
}
}
让我解释一下(但你可以参考Java CountDownLatch)
1.为了确保一个任务必须运行在另一个任务之后,我们需要创建一个Wait
函数来等待它们完成,所以我使用
while(!runnable) {
Monitor.Wait(lk);
}
2.当有任务完成时,我们需要倒计时,如果倒计时为零(这意味着所有任务都完成了)我们需要通知阻塞线程唤醒并处理任务
if(count.decrementAndGet() == 0) {
lock(lk) {
runnable = true;
Monitor.Pulse(lk);
}
}
让我们详细了解 volatile,谢谢
虽然 dung ta van 的“CountDownTask”答案不是我所需要的,但它极大地启发了下面的解决方案(有关详细信息,请参阅它)。基本上我所做的就是添加一些额外的功能,最重要的是:让每个任务都对结果(真或假)进行“投票”。谢谢 dung ta van!
公平地说,dung ta van 的解决方案确实可以保证执行,但事实证明这并不是我所需要的。我的解决方案增加了使执行有条件的能力。
这是我的有效解决方案:
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));
}
}
}
实施上述解决方案非常简单,只需在 UI 线程中创建 SingleVoteTask,然后让影响结果的每个线程进行投票。
背景:
我正在开发一个应用程序,它处理另一个应用程序的大量插件。如果它的主要用途是安全地修改记录较少的文件中的文件记录,以便它们可以被视为一个文件(几乎就像将文件组合成一组记录一样)。为了安全地做到这一点,它会跟踪重要的有关这些文件和对它们所做更改的信息,以便在它们未按预期工作时可以撤消这些更改。
当我的应用程序启动时,它会分析这些文件并将基本属性保存在缓存中(以减少加载时间)。如果缓存中缺少文件,则会检索最重要的内容,然后后台工作人员必须处理该文件以获取更多信息。如果以前修改过的文件已更新为文件的新版本,UI 必须与用户确认这一点并删除其修改数据。所有这些信息,包括有关其修改的信息都存储在缓存中。
我的问题:
我的问题是这两个进程都不能保证运行(确认window 或后台文件处理器)。如果其中任何一个运行,那么缓存必须由主线程更新。我不太了解工作线程,哪个线程 运行 是 BackgroundWorker.RunWorkerCompleted 事件处理程序,以便有效地决定如何保证缓存更新程序在任一(或both) 进程都完成了。
总结一下:如果任一进程是 运行,它们都必须完成并(可能)等待另一个完成,然后才能 运行 缓存更新代码。我该怎么做?
ADJUNCT INFO(我目前的干预似乎效果不是很好):
我在 RunWorkerCompleted 处理程序中有一行等待表单引用为空,然后继续并退出,但这可能是一个错误,因为它有时会锁定我的程序。
SpinWait.SpinUntil(() => overwriteForm == null);
我没有包含更多代码,因为我预计这是一个概念性问题,而不是代码问题。但是,如有必要,如果有帮助,我可以提供代码。
我觉得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);
}
}
让我解释一下(但你可以参考Java CountDownLatch)
1.为了确保一个任务必须运行在另一个任务之后,我们需要创建一个Wait
函数来等待它们完成,所以我使用
while(!runnable) {
Monitor.Wait(lk);
}
2.当有任务完成时,我们需要倒计时,如果倒计时为零(这意味着所有任务都完成了)我们需要通知阻塞线程唤醒并处理任务
if(count.decrementAndGet() == 0) {
lock(lk) {
runnable = true;
Monitor.Pulse(lk);
}
}
让我们详细了解 volatile,谢谢
虽然 dung ta van 的“CountDownTask”答案不是我所需要的,但它极大地启发了下面的解决方案(有关详细信息,请参阅它)。基本上我所做的就是添加一些额外的功能,最重要的是:让每个任务都对结果(真或假)进行“投票”。谢谢 dung ta van!
公平地说,dung ta van 的解决方案确实可以保证执行,但事实证明这并不是我所需要的。我的解决方案增加了使执行有条件的能力。
这是我的有效解决方案:
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));
}
}
}
实施上述解决方案非常简单,只需在 UI 线程中创建 SingleVoteTask,然后让影响结果的每个线程进行投票。