取消后台线程的模式

时间:2009-08-28 07:00:42

标签: design-patterns solid-principles

我在后台线程中运行代码,需要定期检查用户是否要中止工作。

问题是如何在大多数SOLID环境中实现它。

  • 我注入每个班级的ICancel界面。
  • 某处的公共静态成员

非const静态似乎总是会迟早产生问题。 另一方面,“标准”注射的数量正在稳步增长(ILogger,IProgressReporter,......)所以像取消一样简单的事情可能是使用静态的好选择。

还有其他/更好的方法吗?谁有经验分享?

我正在使用WPF和C#,但问题很笼罩。


以下是一个例子:

// this code is in some model assembly

public class BackgroundWorkFactory {
    public IDoingBackgroundWork Worker { 
        get { return new DoingBackgroundWork(new Whatever()); }
    }

internal class DoingBackgroundWork : IDoingBackgroundWork {
    public DoingWork(IWhatever whatever) {
        mWhatever = whatever;
    }
    public void WorkThatCanBeCanceled() {
        while (!Canceled && somethingElse) {
            mWhatever = whatever.DoSomthingElseThatMightAlsoAllowCancel();
            ...
        }
    }
}


// This code is in the GUI Assembly

public void StartWork() {
    IDoingBackgroundWork work = factory.Worker;
    Thread t = new Thread(work->WorkThatCanBeCanceled());
    t.Start();
}

public void StopWork() {
   // ??
}

4 个答案:

答案 0 :(得分:0)

那么你也可以简单地在每个名为volatile的类中创建一个UserHasWork标志,该标志将在true时运行。如果用户取消,请检查标志并停止线程。

然而,这不能在界面中使用,但我确实发现它是执行您想要的有用方式。你总是可以在一个抽象类中使用它,但是这种方法的缺点超过了专业人员。

答案 1 :(得分:0)

IIUC,你提出静态意味着取消所有后台线程。我觉得这不够模块化,并且每个线程都应该单独取消。

OTOH,仅ICancel接口不允许太多重用。我会建议一个班级

class CancelThread{
  private boolean cancelled;
  synchronized void cancel(){cancelled = true;}
  synchronized boolean isCancelled(){return cancelled}
}

每个线程都有其中一个,也许还有一组包含许多此类对象的全局线程。因此,要取消所有线程,您将迭代该集合并为每个线程调用取消。

答案 2 :(得分:0)

也许我在这里有点迷失,但为什么IDoingBackgroundWork不能声明一个简单的Cancel方法,而这个方法又会在基础的DoingBackgroundWorkerBase类中实现?

通过这种方式,您可以通过一种简单的方法从调用者中取消工作线程,默认实现以及修改该实现的能力。是否真的有必要使用注射剂?我认为旧的好“简单”的OO模式可以说明它。

<强>更新

然后我不喜欢这两种方法。我需要知道什么,他必须在某个时候优雅地停止,所以你不能只是注入代码并期望它感激地退出,静态类将添加相同级别的耦合(意味着我必须知道并使用该静态类)。 / p>

我会去定义一个ICancelable接口然后执行:

class DoingBackgroundWorkBase
{
  public Cancel()
  {
    ICancelable cancelableWork = mWhatever as ICancelable;
    if (cancelableWork != null)
      cancelableWork.Cancel();
    else
      this.Abort();
  }  
}

当然

IDoingBackgroundWork work = factory.Worker;
work.Start();

这样IDoingBackgroundWork负责手动启动线程(或者可能是线程本身),并为IWwith提供“优雅的非正常”退出,以及何时不执行ICancelable。

答案 3 :(得分:0)

在尝试了几件事之后我就这样解决了。

创建了一个StopRequest类(看不到需要接口)

public class StopRequest
{
    public void RequestStop() {
        mIsStopRequested = true;
    }

    public void Reset() {
        mIsStopRequested = false;
    }

    public static implicit operator bool(StopRequest stopRequest) {
        return stopRequest.mIsStopRequested;
    }

    private volatile bool mIsStopRequested;
}

将此类注入需要它的每个类(或将其作为方法参数传递)

public void StartWork() {
    mStopRequest = new StopRequest();
    IDoingBackgroundWork work = factory.Worker(mRequestStop);
    mThread = new Thread(work->WorkThatCanBeCanceled());
    mThread.Start();
}

public void StopWork() {
    mStopRequest.RequestStop();
    mThread.Join(timeout);
}

// -----

public class BackgroundWorkFactory {
    public IDoingBackgroundWork Worker(StopRequest stopRequest) { 
        return new DoingBackgroundWork(stopRequest, new Whatever(stopRequest));
    }
}

internal class DoingBackgroundWork : IDoingBackgroundWork {
    public DoingBackgroundWork(StopRequest stopRequest, IWhatever whatever) {
        mStopRequest = stopRequest;
        mWhatever = whatever;
    }

    public void WorkThatCanBeCanceled() {
        while (!mStopRequest && somethingElse) {
            x = mWhatever.DoSomthingElseThatMightAlsoAllowCancel();
            ...
        }
    }
}

这有以下好处
- 不需要静态/单件对象
- 只有具有取消功能的类才会受到影响 - DI框架可以自动注入它 - 单元测试时没有复杂的设置 - 轻量级/快速