假设我们有一个看起来像这样的模板方法
abstract class Worker
{
public void DoJob()
{
BeforJob()
DoRealJob();
AfterJob();
}
abstract void DoRealJob();
}
继承自Wroker
classe的子类应该实现DoRealJob()
方法,
当实现在同一个线程下运行时一切都很好,DoJob()
方法的三个部分按此顺序执行
- BeforJob()
- DoRealJob()
- AfterJob()
醇>
但是当DoRealJob()
在另一个帖子下运行时,AfterJob()
可能会在DoRealJob()
完成之前执行
我的实际解决方案是让子类调用AfterJob()
,但这并不妨碍子类忘记调用它,并且我们失去了模板方法的好处。
是否有其他方法可以获得一致的呼叫顺序,尽管DoRealJob()
阻止了?
答案 0 :(得分:1)
您无法同时获得简单继承(签名和挂钩)并支持代码中的异步操作。
这两个目标是相互排斥的。
继承者必须了解直接(Tasks,async)或间接(事件,{{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();
}
}
DoBefore
和DoAfter
可以用类似的方式表达
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();
}