9

我正在玩Azure Durable 功能。目前,我在InvalidOperationException调用活动后进入了编排功能。它抱怨检测到多线程执行。如果协调器函数之前从不受支持的异步回调中恢复,则可能会发生这种情况

有没有人遇到过这样的问题?我做错了什么?完整代码可以在GitHub上找到

这是编排功能中的一行:

var res = await ctx.CallActivityAsync<int>("LengthCheck", "inputData");

LengthCheck活动函数为:

[FunctionName("LengthCheck")]
public static Task<int> Calc([ActivityTrigger] string input)
{
    var task = Task.Delay(TimeSpan.FromSeconds(5));
    task.Wait();
    return Task.FromResult(input.Length);
}

堆栈跟踪是:

ac6fd5cdd07a4dc9b2577657d65c4f27:函数“InpaintOrchestration(Orchestrator)”,版本“”因错误而失败。原因:System.InvalidOperationException:检测到多线程执行。如果协调器函数之前从不受支持的异步回调中恢复,则可能会发生这种情况。

在 Microsoft.Azure.WebJobs.DurableOrchestrationContext.ThrowIfInvalidAccess()

在 Microsoft.Azure.WebJobs.DurableOrchestrationContext.d__47`1.MoveNext()

从先前抛出异常的位置结束堆栈跟踪

在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)

在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)

在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()

4

2 回答 2

8

每当协调器函数以不受支持的方式执行异步工作时,就会发生此异常。在这种情况下,“不支持”实际上意味着它await用于非持久任务(而“非持久”意味着它是来自除 之外的某些 API 的任务IDurableOrchestrationContext)。

您可以在此处找到有关编排器函数的代码约束的更多信息:https ://docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-code-constraints 。

以下是我快速扫描您的代码时违反的规则:

  • Orchestrator 代码应该是非阻塞的。例如,这意味着没有 I/O,也没有对 Thread.Sleep 或等效 API 的调用。如果编排器需要延迟,它可以使用CreateTimer API。

  • 除了使用IDurableOrchestrationContext API之外, Orchestrator 代码不得启动任何异步操作。例如,没有 Task.Run、Task.Delay 或 HttpClient.SendAsync。持久任务框架在单个线程上执行编排器代码,并且不能与其他异步 API 可以调度的任何其他线程交互。

当我们检测到进行了不受支持的异步调用时,会特别发生此异常。我注意到这段代码中发生了这种情况:

    private static async Task SaveImageLabToBlob(ZsImage imageLab, CloudBlobContainer container, string fileName)
    {
        var argbImage = imageLab
            .Clone()
            .FromLabToRgb()
            .FromRgbToArgb(Area2D.Create(0, 0, imageLab.Width, imageLab.Height));

        using (var bitmap = argbImage.FromArgbToBitmap())
        using (var outputStream = new MemoryStream())
        {
            // modify image
            bitmap.Save(outputStream, ImageFormat.Png);

            // save the result back
            outputStream.Position = 0;
            var resultImageBlob = container.GetBlockBlobReference(fileName);
            await resultImageBlob.UploadFromStreamAsync(outputStream);
        }
    }

进行异步或阻塞调用的正确方法是将它们包装在没有任何这些约束的活动函数中。

在此扩展的最新版本(v1.3.2 和更高版本)中,我们在异常消息中包含了一个指向描述代码约束的文档的链接。

于 2018-03-25T17:22:53.233 回答
1

这也发生在我的持久协调器功能上。我必须摆脱活动函数调用的所有 .ConfigureAwait(false) 结尾。

 //invoking First activity function
        var id = await context.CallActivityAsync<Guid>(Function1, requestModel);
        

        //invoking second activity function that uses data from the first activity function without ConfigureAwait(false)
        var readModel = await context.CallActivityAsync<ReadModel>(Function2, id);
       

        //invoking third activity function that uses data from the second activity function without ConfigureAwait(false)
        await context.CallActivityAsync(Function3, cartReadModel);
        
于 2021-05-07T07:29:19.353 回答