线程上下文中的模板方法

时间:2014-07-06 19:11:32

标签: multithreading design-patterns c#-3.0 template-method-pattern

假设我们有一个看起来像这样的模板方法

abstract class Worker
{
  public void DoJob()
  {
    BeforJob()
    DoRealJob();
    AfterJob();
  }

  abstract void DoRealJob();
}
继承自Wroker classe的

子类应该实现DoRealJob()方法, 当实现在同一个线程下运行时一切都很好,DoJob()方法的三个部分按此顺序执行

  
      
  1. BeforJob()
  2.   
  3. DoRealJob()
  4.   
  5. AfterJob()
  6.   

但是当DoRealJob()在另一个帖子下运行时,AfterJob()可能会在DoRealJob()完成之前执行

我的实际解决方案是让子类调用AfterJob(),但这并不妨碍子类忘记调用它,并且我们失去了模板方法的好处。

是否有其他方法可以获得一致的呼叫顺序,尽管DoRealJob()阻止了?

2 个答案:

答案 0 :(得分:1)

您无法同时获得简单继承(签名和挂钩)并支持代码中的异步操作。

这两个目标是相互排斥的。

继承者必须了解直接Tasksasync)或间接(事件,{{3})中的回调机制},callback functions或其他同步构造)。其中一些是新的,一些是旧的。并且很难说哪一个对于具体的使用情况会更好。

嗯,看起来似乎有一种简单的多线程代码方式,但是如果你的DoRealJob实际上会在另一个Auto(Manual)ResetEvents中运行或者使用某些process,那该怎么办呢?甚至会在您的应用之外执行?

所以:

  • 如果你真的认为你的班级将被用作一些人的基础 异步工作者,那么你应该相应地设计它。
  • 如果不是 - 请勿过度工程。你不能考虑任何可能的 场景。只是记录你的课程,我怀疑 任何人都会尝试异步实现DoRealJob, 特别是如果你给它命名DoRealJobSynchronously。如果有人试图 那么在那种情况下,你的良心可以保持清洁。

修改

  

如果我提供两个版本,同步和,你认为这是正确的吗?   async,DoRealJob和一个标志IsAsynchronous所以我可以决定哪个   一个打电话

正如我已经说过的,我不知道你的实际使用场景。考虑到设计能够有效地处理所有这些设计是不现实的。

还有两个非常重要的问题与您的整体Worker及其DoJob方法有关:

1)您必须确定是希望DoJob方法是同步还是异步,还是要同时拥有同步和异步版本?它与您的问题没有直接关系,但它仍然是非常重要的设计决策,因为它会对您的对象模型产生很大影响。这个问题可以改为:

您希望DoJob方法在调用之后阻止任何操作,直到它完成其工作,或者您是否想要将其称为某种StartJob方法,这只会启动实际处理但是当作业结束(或手动停止)时,由其他机制通知您:

        //----------------Sync worker--------------------------
        SyncWorker syncWorker = CreateSyncStringWriter("The job is done");
        Console.WriteLine("SyncWorker will be called now");

        syncWorker.DoJob(); // "The job is done" is written here

        Console.WriteLine("SyncWorker call ended");


        //----------------Async worker--------------------------
        Int32 delay = 1000;
        AsyncWorker asyncWorker = CreateAsyncStringWriter("The job is done", delay);
        Console.WriteLine("AsyncWorker will be called now");

        asyncWorker.StartDoJob(); // "The job is done" won't probably be written here

        Console.WriteLine("AsyncWorker call ended");
        // "The job is done" could be written somewhere here.

2)如果您希望DoJob是异步(或具有异步版本),您应该考虑是否要有一些机制在DoJob完成处理时通知 - remote job queuing,或者它与你完全无关,无论何时或是否结束。

<强> SO:

你对这两个问题有答案吗?

  • 如果是 - 那很好。
  • 如果没有 - 改进并考虑您的要求。
  • 如果您仍然不确定 - 坚持使用简单的同步方法。

但是,如果您认为需要一些基于异步的基础架构,那么考虑到它是C#3.0,您应该使用Async Programming Patterns

为什么Asynchronouse Programming Model?因为IAsyncResult接口虽然很麻烦,但非常通用,可以this one and not the event based,简化了将来向更高版本.NET版本的过渡。

这将是:

 /// <summary>
 /// Interface for both  the sync and async job.
 /// </summary>
 public interface IWorker
{
    void DoJob();

    IAsyncResult BeginDoJob(AsyncCallback callback);

    public void EndDoJob(IAsyncResult asyncResult);
}

/// <summary>
/// Base class that provides DoBefore and DoAfter methods
/// </summary>
public abstract class Worker : IWorker
{
    protected abstract void DoBefore();
    protected abstract void DoAfter();


    public IAsyncResult BeginDoJob(AsyncCallback callback)
    {
        return new Action(((IWorker)this).DoJob)
            .BeginInvoke(callback, null);
    }
    //...
}

public abstract class SyncWorker : Worker
{
    abstract protected void DoRealJobSync();

    public void DoJob()
    {
        DoBefore();
        DoRealJobSync();
        DoAfter();
    }
}

public abstract class AsyncWorker : Worker
{
    abstract protected IAsyncResult BeginDoRealJob(AsyncCallback callback);
    abstract protected void EndDoRealJob(IAsyncResult asyncResult);

    public void DoJob()
    {
        DoBefore();
        IAsyncResult asyncResult = this.BeginDoRealJob(null);
        this.EndDoRealJob(asyncResult);
        DoAfter();
    }
}

P.S。:此示例不完整且未经过测试 P.P.S:您也可以考虑使用代理代替抽象(虚拟)方法来表达您的工作:

public class ActionWorker : Worker
{
    private Action doRealJob;
    //...

    public ActionWorker(Action doRealJob)
    {
        if (doRealJob == null)
            throw new ArgumentNullException();

        this.doRealJob = doRealJob;
    }

    public void DoJob()
    {
        this.DoBefore();
        this.doRealJob();
        this.DoAfter();
    }
}

DoBeforeDoAfter可以用类似的方式表达 P.P.P.S: Action委托是一个3.5结构,因此您可能必须定义自己的委托,接受零参数并返回void。

public delegate void MyAction()

答案 1 :(得分:0)

考虑将DoRealJob更改为DoRealJobAsync并为其指定Task返回值。所以你可以等待最终的异步结果。

所以你的代码看起来像

abstract class Worker
{
  public void DoJob()
  {
    BeforJob()
    await DoRealJobAsync();
    AfterJob();
  }

  abstract Task DoRealJob();
}

如果您没有.net 4.0并且不想使用异步的旧3.0 CTP,您可以使用normale任务库样式:

  abstract class Worker
    {
      public void DoJob()
      {
        BeforJob()
        var task = DoRealJobAsync();
         .ContinueWith((prevTask) =>
              {
                  AfterJob()
               });
      }

      abstract Task DoRealJob();
    }