You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Dart/Flutter如何防止异步函数被并发重复调用?

解决Dart异步函数重复调用的问题

问题背景

我在Flutter中有一个不能被同时调用两次的异步函数,示例代码如下:

class DatabaseHelper {
  Future<void> add100elements() async {
    // 从数据库查询并添加100条数据到本地列表
    var elementsToAdd = await db.rawQuery('select * from quotes where id > ? limit 100', this.currentId);
    this.localList.addAll(elementsToAdd);
    this.currentId += 100;
  }
}

应用中可能有两处几乎同时调用add100elements,这会导致重复执行,添加相同的100条数据。我自己实现了一个手动的解决方案:

class DatabaseHelper {
  bool isRunning = false;

  Future<void> add100elements() async {
    if (isRunning) return;
    isRunning = true;

    var elementsToAdd = await db.rawQuery('select * from quotes where id > ? limit 100', this.currentId);
    this.localList.addAll(elementsToAdd);
    this.currentId += 100;
    isRunning = false;
  }
}

但这种方式比较繁琐,想知道Dart有没有内置的实现方式或者更优的解决方案。


更优雅的手动改进方案

你的布尔标记方案存在一个隐患:如果函数执行过程中抛出异常,isRunning会一直保持true,导致后续所有调用都无法执行。可以用try/finally块修复这个问题:

class DatabaseHelper {
  bool isRunning = false;

  Future<void> add100elements() async {
    if (isRunning) return;
    isRunning = true;
    try {
      var elementsToAdd = await db.rawQuery('select * from quotes where id > ? limit 100', this.currentId);
      this.localList.addAll(elementsToAdd);
      this.currentId += 100;
    } finally {
      // 无论成功还是失败,都重置标记
      isRunning = false;
    }
  }
}

如果希望后续调用不是直接返回,而是等待前一次执行完成后再执行(即任务排队),可以用一个Future变量跟踪当前执行的任务,实现更健壮的控制:

class DatabaseHelper {
  Future<void>? _currentTask;

  Future<void> add100elements() async {
    // 如果有正在执行的任务,先等待它完成
    if (_currentTask != null) {
      await _currentTask;
      return;
    }
    // 标记当前执行的任务
    _currentTask = _performAdd();
    try {
      await _currentTask;
    } finally {
      // 执行完成后清空标记
      _currentTask = null;
    }
  }

  // 把实际逻辑抽离成单独方法,让代码更清晰
  Future<void> _performAdd() async {
    var elementsToAdd = await db.rawQuery('select * from quotes where id > ? limit 100', this.currentId);
    this.localList.addAll(elementsToAdd);
    this.currentId += 100;
  }
}

使用社区同步锁库

Dart标准库没有内置的互斥锁,但可以使用社区维护的synchronized包,它提供的Mutex类能简洁实现临界区控制,自动处理锁的获取和释放:

import 'package:synchronized/synchronized.dart';

class DatabaseHelper {
  final _mutex = Mutex();

  Future<void> add100elements() async {
    // 同一时间只有一个调用能进入这个代码块
    await _mutex.synchronized(() async {
      var elementsToAdd = await db.rawQuery('select * from quotes where id > ? limit 100', this.currentId);
      this.localList.addAll(elementsToAdd);
      this.currentId += 100;
    });
  }
}

这种方式无需手动管理状态,即使执行中抛出异常,锁也会自动释放,代码更简洁可靠。


内容的提问来源于stack exchange,提问作者DevShark

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 22:43:24