Dart:如何在异步函数中管理并发

J F*_*J F 6 async-await dart

我真的很喜欢Dart中的async/await模式.它允许我编写可读的方法.

但是,有一些问题是有问题的,尤其是一些,我根本不知道管理的热情.

问题是在方法中使用异步和多个等待,我们在方法中引入了并发性.例如,如果我有一个方法:

Future<int> foo(int value) async {
await foo2();
await foo3();
await foo4();
int ret = foo5(value);
return ret;
}
Run Code Online (Sandbox Code Playgroud)

嗯,这是一个非常简单的例子.这里的问题是,对于每个await,该方法都放在事件循环中.没关系,当你理解它时,但这并不妨碍你的应用程序再次调用方法,因为它已经重新调整了一个值.

考虑该方法是否正在管理类的实例所共有的数据,而不是方法本身.

所以,我尝试了以下解决方案:

bool isWorking = false;

Future<int> foo(int value) async {
if (isWorking) return foo(value);
isWorking = true;

await foo2();
await foo3();
await foo4();
int ret = foo5(value);

isWorking = False;

return ret;
}
Run Code Online (Sandbox Code Playgroud)

据我所知,调用future方法会立即将它放在事件循环中,所以我认为方法的并发调用的执行被推迟到第一个结束时.但事实并非如此,程序进入无限循环.

任何人都可以给我一个解释和解决这个问题的方法吗?

编辑:一般来说,我认为与其他语言一样,有一个synchronized关键字,意思是该方法,如果第二次调用将等到第一次结束,这可能会很有趣.就像是:

Future<int> foo(int value) async synchronized {
Run Code Online (Sandbox Code Playgroud)

编辑2:

我真的很兴奋,因为我觉得我已经解决了这个问题,我已经有很长一段时间了.感谢Argenti,尤其是Alexandre给我的解决方案.我只是简单地重新构建了解决方案以便于重用(至少对我而言)并在此处将其发布在我创建的类中以及如何将其用于可能需要它的人的示例(尝试自担风险;-)).我使用了mixin,因为我发现它很实用,但如果你愿意,可以单独使用Locker类.

myClass extends Object with LockManager {

  Locker locker = LockManager.getLocker();

  Future<int> foo(int value) async {

   _recall() {
      return foo(value);
   } 

   if (locker.locked) {
     return await locker.waitLock();
   }
   locker.setFunction(_recall);
   locker.lock();

   await foo2();
   await foo3();
   await foo4();
   int ret = foo5(value);

   locker.unlock();

   return ret;
  }
}
Run Code Online (Sandbox Code Playgroud)

这堂课是:

import 'dart:async';

class LockManager {

static Locker getLocker() => new Locker();

}

class Locker {

  Future<Null> _isWorking = null;
  Completer<Null> completer;
  Function _function;
  bool get locked => _isWorking != null;

  lock() {
    completer = new Completer();
    _isWorking = completer.future;
  }

  unlock() {
    completer.complete();
    _isWorking = null;
  }

  waitLock() async {
      await _isWorking;
      return _function();
  }

  setFunction(Function fun) {
    if (_function == null) _function = fun;
  }

}
Run Code Online (Sandbox Code Playgroud)

我已经以这种方式构造了代码,以便您可以在类中的多个方法中轻松使用它.在这种情况下,每个方法需要一个Locker实例.我希望它有用.

s k*_*s k 11

现在已经是2021年了,我们可以使用synchronized 3.0.0

var lock = new Lock();
Future<int> foo(int value) async {
    int ret;
    await lock.synchronized(() async {
        await foo2();
        await foo3();
        await foo4();
        ret = foo5(value);
    }
    return ret;
}
Run Code Online (Sandbox Code Playgroud)


Ale*_*uin 5

取而代之的是布尔的,你可以使用Future完成者,以达到你想要的东西:

Future<Null> isWorking = null;

Future<int> foo(int value) async {
  if (isWorking != null) {
    await isWorking; // wait for future complete
    return foo(value);
  }

  // lock
  var completer = new Completer<Null>();
  isWorking = completer.future;

  await foo2();
  await foo3();
  await foo4();
  int ret = foo5(value);

  // unlock
  completer.complete();
  isWorking = null;

  return ret;
}
Run Code Online (Sandbox Code Playgroud)

第一次的方法调用isWorkingnull,不进入if部分,并创建isWorking一个未来,这将是在方法的最后完成。如果foo在第一个调用完成Future之前进行了另一个调用isWorking,则此调用进入if区域,并等待Future isWorking完成。对于在第一个呼叫完成之前可以进行的所有呼叫,情况都是相同的。一旦第一个呼叫完成(并isWorking设置为null),将通知等待的呼叫,它们将再次呼叫foo。其中一个将foo作为第一个呼叫进入,并且将完成相同的工作流程。

请参阅https://dartpad.dartlang.org/dceafcb4e6349acf770b67c0e816e9a7,以更好地了解工作流程。


lrn*_*lrn 5

答案很好,这是防止互操作异步操作的“互斥”的另一种实现。

class AsyncMutex {
  Future _next = new Future.value(null);
  /// Request [operation] to be run exclusively.
  ///
  /// Waits for all previously requested operations to complete,
  /// then runs the operation and completes the returned future with the
  /// result.
  Future<T> run<T>(Future<T> operation()) {
    var completer = new Completer<T>();
    _next.whenComplete(() {
      completer.complete(new Future<T>.sync(operation));
    });
    return _next = completer.future;
  }
}
Run Code Online (Sandbox Code Playgroud)

它没有很多功能,但是很简短,希望可以理解。