从任意(同步)方法

时间:2017-02-14 18:12:44

标签: c# asynchronous async-await hangfire

如果我有定期的方法通话,我需要启动'使用async / await,启动它的最佳方法是什么?在没有详细说明的情况下,我使用Hangfire来处理作业,并且由于某些情况我认为超出了问题的范围,因此Hangfire作业同步运行,但后来我想要以后启动异步并等待,以便实际的工作代码'可以根据需要/期望使用async / await。是否低于启用异步和等待的最佳方式?

public void SynchMethod()
{
    var inputPackage = XElement.Parse( "NormallyPassedIn" );
    var hangfireJob = CreateJob( inputPackage );

    Task.Run( async () =>
    {
        // This Execute method implementation wants to use await on several
        // helper methods it calls, so this is how I thought to allow for that
        await hangfireJob.Execute( inputPackage );
    } ).GetAwaiter().GetResult();
}

更新2:阻止尽可能高......

因此,尝试将Stephen的阻止建议尽可能高(基本上是在第一次/仅跨域调用),我尝试将代码更改为以下内容:

var appDomain = AppDomain.CreateDomain( info.ApplicationName, AppDomain.CurrentDomain.Evidence, info );
...
instance = appDomain.CreateInstanceAndUnwrap( jobInvokerType.Assembly.FullName, jobInvokerType.FullName ) as JobInvoker;
...
instance.Process( this, inputPackage.ToString() ).GetAwaiter().GetResult();

使用过程功能:

public async Task Process( IHangfireJobContext jobContext, string inputPackageXml )
{
...
    var hangfireJob = CreateJob( assembly, jobTypeName );

    await hangfireJob.Execute( inputPackage, jobContext );
}

请记住hangfireJob.Execute是真实的'我希望能够使用async / await的方法。只要hangfireJob.Execute使用await,就会抛出以下异常:

  

类型   ' System.Threading.Tasks.Task`1 [[System.Threading.Tasks.VoidTaskResult,   mscorlib,版本= 4.0.0.0,文化=中性,   公钥= b77a5c561934e089]]'在Assembly&#m; mscorlib中,   Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089'是   没有标记为可序列化。

     

服务器堆栈跟踪:at   System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType   输入)   System.Runtime.Serialization.FormatterServices.GetSerializableMembers(类型   type,StreamingContext context)at   System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo()   在   System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(对象   obj,ISurrogateSelector surrogateSelector,StreamingContext context,   SerObjectInfoInit serObjectInfoInit,IFormatterConverter转换器,   ObjectWriter objectWriter,SerializationBinder binder)at   System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(对象   obj,ISurrogateSelector surrogateSelector,StreamingContext context,   SerObjectInfoInit serObjectInfoInit,IFormatterConverter转换器,   ObjectWriter objectWriter,SerializationBinder binder)at   System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(对象   graph,Header [] inHeaders,__BinaryWerer serWriter,Boolean fCheck)
  在   System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(流   serializationStream,Object graph,Header [] headers,Boolean fCheck)
  在   System.Runtime.Remoting.Channels.CrossAppDomainSerializer.SerializeMessageParts(ArrayList的   argsToSerialize)at   System.Runtime.Remoting.Messaging.SmuggledMethodReturnMessage..ctor(IMethodReturnMessage   先生   System.Runtime.Remoting.Messaging.SmuggledMethodReturnMessage.SmuggleIfPossible(即时聊天   msg)at   System.Runtime.Remoting.Channels.CrossAppDomainSink.DoDispatch(字节[]   reqStmBuff,SmuggledMethodCallMessage走私了Mcm,   SmuggledMethodReturnMessage&安培;走私的人   System.Runtime.Remoting.Channels.CrossAppDomainSink.DoTransitionDispatchCallback(对象[]   参数)

     

在[0]处重新抛出异常:at   System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(即时聊天   reqMsg,IMessage retMsg)at   System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData&安培;   msgData,Int32类型)at   BTR.Evolution.Hangfire.JobInvoker.Process(IHangfireJobContext   jobContext,String inputPackageXml)at   BTR.Evolution.Hangfire.JobInvoker.Invoke(XElement inputPackage,   PerformContext performContext,IJobCancellationToken   cancelToken)   C:\ BTR \ Source \ Evolution \ BTR.Evolution.Hangfire \ JobInvoker.cs:第86行

所以我改回了:

public void Process( IHangfireJobContext jobContext, string inputPackageXml )

并将.GetAwaiter().GetResult()移至hangfireJob.Execute()的末尾:

hangfireJob.Execute( inputPackage, jobContext ).GetAwaiter().GetResult();

然后一切正常。将斯蒂芬的答案标记为正确。不知道为什么我无法阻止第一个/唯一一个跨越域名的电话,但可能是预期的。

更新1:AppDomain创建/推理

所以我想我会根据下面的评论更新问题。我真正运行的工作流程如下,我遇到的主要问题是我正在创建一个AppDomain并跨域调用。

  1. Hangfire启动我的工作(JobInvoker.Invoke)以运行(您可以让您的工作成为同步或异步工作)。最初,我试图像public async Task Invoke( XElement inputPackage, PerformContext performContext, IJobCancellationToken cancellationToken )一样异步运行。

  2. JobInvoker.Invoke通过var appDomain = AppDomain.CreateDomain( info.ApplicationName, AppDomain.CurrentDomain.Evidence, info );

  3. 创建AppDomain
  4. JobInvoker.Invoke通过instance = appDomain.CreateInstanceAndUnwrap( jobInvokerType.Assembly.FullName, jobInvokerType.FullName )创建对象。

  5. 我尝试调用带有签名instance.Process的{​​{1}}方法。

  6. public async Task Process( IHangfireJobContext jobContext, string inputPackageXml )通过反映使用上面instance.Process的代码创建了一个对象。这个var hangfireJob = CreateJob()对象的方法是签名中需要hangfireJob

  7. async通过instance.Process调用hangfireJob。

  8. await hangfireJob.Execute()签名为hangfireJob.Execute

  9. 然后代码看起来好得多,因为我只需将public async Task Execute( XElement inputPackage, IHangfireJobContext jobContext )放在我的所有方法上,然后随意使用async。但是只要await尝试使用hangfireJob.Execute并且我收到以下异常(请记住这是在单独的AppDomain中运行,因此异常):

      

    类型   ' System.Threading.Tasks.Task`1 [[System.Threading.Tasks.VoidTaskResult,   mscorlib,版本= 4.0.0.0,文化=中性,   公钥= b77a5c561934e089]]'在Assembly&#m; mscorlib中,   Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089'是   没有标记为可序列化。

    这就是为什么我尝试引入'来自await内的异步编码(实际上由我的原始instance.Process表示),因为我的SynchMethod方法确实需要hangfireJob.Execute签名,所以我可以做一些async来电。

    鉴于此信息,可能所有评论都不再适用?如果您认为它们或者它是否像执行await一样简单(并删除await hangfireJob.Execute( inputPackage ).GetAwaiter().GetResult()包装器),请告诉我。

1 个答案:

答案 0 :(得分:2)

在一般情况下,您应该强烈避免阻止异步代码。

此规则的一个例外是控制台应用上的Main方法。

Win32服务的一个有趣的怪癖是它们在架构上与Console应用程序类似。具体来说,他们有一个“主”,在服务停止之前不应退出,并且他们没有提供SynchronizationContext。因此,在您的服务实现中阻止是适当的。

但是,我建议您遵循与在控制台应用程序中阻止相同的最佳做法:仅在一个点上阻止,尽可能远离堆栈。

就具体情况而言,GetAwaiter().GetResult()就足够了;没有Task.Run