C# 如何将带有ref参数的函数转换为异步函数?

C# 如何将带有ref参数的函数转换为异步函数?,c#,iis,android-asynctask,async-await,C#,Iis,Android Asynctask,Async Await,我有以下函数,我想将其转换为异步/非锁定函数 以下是当前形式的函数: private static void BlockForResponse(ref bool localFlag) { int count = 0; while (!localFlag) { Thread.Sleep(200); if (count++ > 50) // 200 * 50 = 10 second

我有以下函数,我想将其转换为异步/非锁定函数

以下是当前形式的函数:

   private static void BlockForResponse(ref bool localFlag)
    {
        int count = 0;
        while (!localFlag)
        {
            Thread.Sleep(200);
            if (count++ > 50)   // 200 * 50 = 10 seconds
            {
                //timeout
                throw new TimeOutException();
            }
        }
    }
以下是我的尝试:

  private static async Task BlockForResponse(ref bool localFlag)
    {
        int count = 0;
        while (!localFlag)
        {
            await Task.Delay(200);
            if (count++ > 50)   // 200 * 50 = 10 seconds
            {
                //timeout
                throw new TimeOutException();
            }
        }
    }
然而,我得到一个编译错误,说异步函数不能有ref或out参数。然而,这是该功能的核心功能

是否可以将其转换为异步函数

代码说明:

我必须承认这是一段奇怪的代码,让我试着解释一下它试图做什么:

所以有一个第三方dll,我需要使用。它为我提供服务,很遗憾我无法控制这个dll

它的工作方式, 我在dll中调用一个命令,为它提供一个回调函数,它在完成任务后调用该函数

我只有在得到电话结果后才能继续做我想做的事情。因此需要使用此功能

我调用dll,为其提供回调函数:

    private bool _commandFlag = false;
    private bool _commandResponse;

    public async Task ExecuteCommand(string userId, string deviceId)
    {
        var link = await LinkProviderAsync.GetDeviceLinkAsync(deviceId, userId);
        try
        {               
            //execute command
            if (link.Command(Commands.ConnectToDevice, CallBackFunction))
            {
                BlockForResponse(ref _commandFlag);
                return;     //Received a response
            }
            else
            {   //Timeout Error                    
                throw new ConnectionErrorException();
            }
        }
        catch (Exception e)
        {
            throw e;
        }
    }

    private void CallBackFunction(bool result)
    {
        _commandResponse = result;
        _commandFlag = true;
    }

结合
async
ref
的问题是
async
函数中的代码即使在方法返回后也可以运行。所以,如果你做了如下事情:

async Task BlockForResponseAsync(ref bool localFlag)
{
    while (!localFlag)
    {
        ...
    }
}

void SomeMethod()
{
    bool flag = false;
    BlockForResponseAsync(ref flag); // note: no await here
}
然后,在返回
SomeMethod()
后,局部变量
标志将停止存在,但是仍然可以执行引用该变量的
BlockForResponseAsync()
。这就是上面的代码无法编译的原因

基本上,您需要的是闭包,在C#中,
ref
不创建闭包,但lambdas创建闭包。这意味着您可以这样编写方法:

async Task BlockForResponseAsync(Func<bool> localFlagFunc)
{
    while (!localFlagFunc())
    {
        ...
    }
}
bool flag = false;
var task = BlockForResponseAsync(() => flag);

// other code here

flag = true;
await task; // to make sure BlockForResponseAsync() completed successfully

这样也能更好地表明你的意图
ref
通常的意思是:“给我一个带有某个值的变量,我会改变该值”,这不是您想要的。另一方面,
Func
意味着“给我一些可以用来检索某些值的东西,可能会多次使用”。

async
ref
组合起来的问题是
async
函数中的代码即使在方法返回后也可以运行。所以,如果你做了如下事情:

async Task BlockForResponseAsync(ref bool localFlag)
{
    while (!localFlag)
    {
        ...
    }
}

void SomeMethod()
{
    bool flag = false;
    BlockForResponseAsync(ref flag); // note: no await here
}
然后,在返回
SomeMethod()
后,局部变量
标志将停止存在,但是仍然可以执行引用该变量的
BlockForResponseAsync()
。这就是上面的代码无法编译的原因

基本上,您需要的是闭包,在C#中,
ref
不创建闭包,但lambdas创建闭包。这意味着您可以这样编写方法:

async Task BlockForResponseAsync(Func<bool> localFlagFunc)
{
    while (!localFlagFunc())
    {
        ...
    }
}
bool flag = false;
var task = BlockForResponseAsync(() => flag);

// other code here

flag = true;
await task; // to make sure BlockForResponseAsync() completed successfully
这样也能更好地表明你的意图
ref
通常的意思是:“给我一个带有某个值的变量,我会改变该值”,这不是您想要的。另一方面,
Func
的意思是“给我一些我可以用来检索某些值的东西,可能多次”

按照它的工作方式,我在dll中调用一个命令,为它提供一个回调函数,它在完成任务后调用该函数

然后,您真正想要的是使用
TaskCompletionSource
创建一个TAP方法

按照它的工作方式,我在dll中调用一个命令,为它提供一个回调函数,它在完成任务后调用该函数

然后,您真正想要的是使用
TaskCompletionSource
创建一个TAP方法


可能重复您实际想要实现的目标?这看起来是一个相当奇怪的方法开始。。。也许你只是想用一个
CancellationToken
来代替,或者一个
Task
来让你知道任务何时完成?我同意Jon(嗨Jon!:)。这里发生的事情比不能传递by-ref更可疑。至少,标志上缺少易失性读取,这可能导致线程安全问题。正如Jon所指出的,代码的语义似乎与某些取消场景一致,这表明最好使用预先存在的.NET机制来支持(例如CancellationToken,或者根据具体场景监控跨线程信号)。请参阅我在问题中更新的信息。这将有助于更好地理解手头的代码/问题;此代码应立即停止。。正确的?那么这不是正确的方法。。使用计时器,它会在每一个滴答声中检查localFlag。可能与您实际想要实现的目标重复?这看起来是一个相当奇怪的方法开始。。。也许你只是想用一个
CancellationToken
来代替,或者一个
Task
来让你知道任务何时完成?我同意Jon(嗨Jon!:)。这里发生的事情比不能传递by-ref更可疑。至少,标志上缺少易失性读取,这可能导致线程安全问题。正如Jon所指出的,代码的语义似乎与某些取消场景一致,这表明最好使用预先存在的.NET机制来支持(例如CancellationToken,或者根据具体场景监控跨线程信号)。请参阅我在问题中更新的信息。这将有助于更好地理解手头的代码/问题;此代码应立即停止。。正确的?那么这不是正确的方法。。使用计时器,每次滴答地检查localFlag..嗯,非常有趣的方法,我必须承认我不知道有这样的事情存在。我一定会研究这个。嗯,非常有趣的方法,我必须承认我不知道有这样的事情存在。我一定会调查的。非常好的解决方案。这个解决方案在可伸缩性方面将如何执行?我在asp.net web api中与许多并发用户一起调用此
ExecuteCommand
。因此我开始发炎