1

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

问题是如何在大部分 SOLID 环境中实现这一点。

  • ICancel我注入每个类的接口。
  • 某处的公共静态成员

非常量静态似乎总是迟早会产生问题。另一方面,“标准”注入的数量正在稳步增长(ILogger、IPProgressReporter 等),因此像取消这样的简单操作可能是使用静态的不错选择。

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

我正在使用 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

4 回答 4

0

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

但是,这不能在界面中使用,但是我确实发现它是执行您想要的操作的有用方法。您也可以始终在抽象类中使用它,但这种方法的缺点超过了优点。

于 2009-08-28T07:08:56.070 回答
0

IIUC,您建议静态意味着取消所有后台线程。我觉得这不够模块化,每个线程都应该可以单独取消。

OTOH,单独的 ICancel 接口不允许太多重用。我会提议一堂课

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

每个线程都会有一个,也许还有许多这样的对象的全局集。因此,要取消所有线程,您需要遍历该集合并为每个线程调用取消。

于 2009-08-28T07:12:59.730 回答
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 框架可能会自动注入
- 单元测试时无需复杂的设置
- 轻量/快速

于 2009-09-15T09:37:44.173 回答
0

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

这样,您将有一个简单的方法来取消调用者的工作线程、默认实现以及修改该实现的能力。真的有必要为此使用注射吗?我认为旧的好的“简单”OO模式涵盖了它。

更新:

那我不喜欢这两种方法。IWhatever 需要知道他必须在某个时间优雅地停止,这样您就不能只是注入代码并期望它感激地退出,并且静态类将添加相同级别的耦合(意味着 IWhatever 必须知道并使用该静态类)。

我会去定义一个 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 负责手动启动线程(或者可能是线程本身),并在 IWhatever 也没有实现 ICancelable 时提供“优雅的不优雅”退出。

于 2009-08-28T08:28:53.710 回答