Flutter互斥锁管理插件get_mutex的使用

发布于 1周前 作者 yibo5220 来自 Flutter

Flutter互斥锁管理插件get_mutex的使用

GetMutex 是一个用于 Dart 的强大、高效且无死锁的同步解决方案。它使并发编程变得简单,同时不会牺牲性能。

为什么使用GetMutex?

在并发编程的世界里,竞态条件可能会破坏你的代码,导致不可预测的结果。GetMutex 可以消除这些风险,并提供以下优势:

  • 🚀 高性能:在我们的基准测试中,其性能比简单的 await 解决方案高出两倍。
  • 🛡️ 可靠保护:精确锁定关键部分,确保数据完整性。
  • 🧠 简洁API:用户友好且直观的API,使并发编程不再令人望而生畏。
  • 🔬 100% 测试覆盖率:我们非常重视你的数据安全,这从我们的测试覆盖率中可以看出。

让我们通过一个实际案例来说明为什么 GetMutex 是不可或缺的。

银行账户问题:并发混乱的典型案例

假设你正在开发一个银行应用程序。如果没有适当的同步机制,并发转账可能导致财务差异。以下是不使用 GetMutex 的情况:

没有同步(容易发生竞态条件)

假设你正在开发一个银行应用程序。如果没有适当的同步机制,你很容易因并发转账而导致财务灾难。让我们看看没有 GetMutex 的情况:

import 'dart:async';

class BankAccount {
  final String accountNumber;
  double balance;

  BankAccount(this.accountNumber, this.balance);

  Future<bool> transfer(BankAccount targetAccount, double amount) async {
    if (amount > balance) {
      return false; // 资金不足
    }

    await Future.delayed(Duration(milliseconds: 50)); // 模拟处理时间

    balance -= amount;

    await Future.delayed(Duration(milliseconds: 50)); // 模拟一些延迟

    targetAccount.balance += amount;

    return true;
  }
}

void main() async {
  final accountA = BankAccount('Account A', 1000);
  final accountB = BankAccount('Account B', 500);

  // 模拟多个并发转账
  final transfers = [
    accountA.transfer(accountB, 200), // Account A: $800, Account B: $700
    accountA.transfer(accountB, 300), // Account A: $500, Account B: $1000
    accountB.transfer(accountA, 100), // Account A: $600, Account B: $900
    accountB.transfer(accountA, 700), // Account A: $1300, Account B: $200
  ];

  final results = await Future.wait(transfers);

  print('Transfer Results:');
  for (var result in results) {
    print(result ? 'Transfer succeeded' : 'Transfer failed');
  }
  // 可能的输出:
  // Transfer succeeded
  // Transfer succeeded
  // Transfer succeeded
  // Transfer failed

  print('Final Balances:');
  print('${accountA.accountNumber}: \$${accountA.balance}');
  print('${accountB.accountNumber}: \$${accountB.balance}');
  // 可能的输出:
  // Account A: $600.0
  // Account B: $900.0
}

问题

你可能期望最终余额为:

  • Account A: $1300.0
  • Account B: $200.0

但由于竞态条件,你可能会得到:

  • Account A: $600.0
  • Account B: $900.0

钱似乎从空气中凭空出现!这是因为多个转账操作在没有适当同步的情况下并发进行。

使用await解决(提示:这很慢)

你可能会想,“我只需在每个操作前使用 await!” 当然,这可以工作,但代价是什么?

void main() async {
  final accountA = BankAccount('Account A', 1000);
  final accountB = BankAccount('Account B', 500);

  final clockWatch = Stopwatch()..start();
  await accountA.transfer(accountB, 200);
  await accountA.transfer(accountB, 300);
  await accountB.transfer(accountA, 100);
  await accountB.transfer(accountA, 700);
  clockWatch.stop();
  print('Elapsed time using await: ${clockWatch.elapsedMilliseconds} ms');
  // 输出:Elapsed time using await: 209 ms
}

使用GetMutex:安全且快速

现在,让我们看看 GetMutex 如何优雅且快速地处理这种情况:

import 'package:get_mutex/get_mutex.dart';

class BankAccount {
  final String accountNumber;
  double balance;
  final mutex = Mutex();

  BankAccount(this.accountNumber, this.balance);

  Future<bool> transfer(BankAccount targetAccount, double amount) async {
    return await mutex.protectWrite(() async {
      if (amount > balance) return false;
      balance -= amount;
      await targetAccount.deposit(amount);
      return true;
    });
  }

  Future<void> deposit(double amount) async => balance += amount;
}

void main() async {
  final accountA = BankAccount('Account A', 1000);
  final accountB = BankAccount('Account B', 500);

  final transfers = [
    accountA.transfer(accountB, 200),
    accountA.transfer(accountB, 300),
    accountB.transfer(accountA, 100),
    accountB.transfer(accountA, 700),
  ];

  final clockWatch = Stopwatch()..start();
  await Future.wait(transfers);
  clockWatch.stop();
  print('Elapsed time using mutex: ${clockWatch.elapsedMilliseconds} ms');
  // 输出:Elapsed time using mutex: 107 ms

  print('Final Balances:');
  print('${accountA.accountNumber}: \$${accountA.balance}'); // Account A: $1300.0
  print('${accountB.accountNumber}: \$${accountB.balance}'); // Account B: $200.0
}

使用 GetMutex 不仅可以保证账户的安全性和转账的原子性,而且还可以将处理时间缩短一半!

功能让你会心一笑

  • 🔄 简单且可重入的互斥锁:锁一次或多次,我们都为你准备好了。
  • 📚 读写锁:因为有时候共享才是最好的(并且更高效)。
  • ⏱️ 超时和取消:因为即使是互斥锁也不应该永远等待。
  • 🪜 灵活策略:公平、读者优先或写者优先——由你决定。

入门指南

  1. pubspec.yaml 中添加 GetMutex

    dependencies:
      get_mutex: ^1.0.0
    
  2. 运行 dart pub get

  3. 导入并享受:

    import 'package:get_mutex/get_mutex.dart';
    
    final mutex = Mutex();
    await mutex.protectWrite(() {
      // 你的关键部分在这里
      print('Writing safely!');
    });
    

高级用法

GetMutex 会随着你的需求增长。需要更多控制?我们已经为你准备好了:

final mutex = Mutex();

// 优化多个读取
await mutex.protectRead(() => print('Reading in parallel'));

// 确保写入时独占访问
await mutex.protectWrite(() => print('Writing exclusively'));

// 设置超时以避免死锁
await mutex.protectWrite(() => longOperation(), timeout: Duration(seconds: 5));

// 使用取消令牌进行更多控制
final token = CancellationToken();
await mutex.protectWrite(() => cancelableOperation(), cancellationToken: token);

我们也支持原始读写锁,以便对共享资源进行细粒度控制,但同样易于使用(然而,我们建议在大多数情况下使用 MutexRawReadWriteMutex 更低级别,并且不支持可重入锁):

final rwMutex = RawReadWriteMutex();

await rwMutex.protectRead(() => print('Reading in parallel'));
await rwMutex.protectWrite(() => print('Writing exclusively'));

Future<T> myCustomProtectWrite<T>(Future<T> Function() action, {CancellationToken? cancellationToken, Duration? timeout}) async {
  await rwMutex.acquireWrite(cancellationToken: cancellationToken, timeout: timeout);
  try {
    return await action();
  } finally {
    rwMutex.releaseWrite();
  }
}

为了简化,我们有一个 GetMutex 类,包含所有可用的互斥锁:

final mutex = GetMutex.mutex();
await mutex.protectWrite(() => print('Writing safely!'));

final rwMutex = GetMutex.rawReadWriteMutex();
await rwMutex.protectRead(() => print('Reading in parallel'));

final simpleMutex = GetMutex.simpleMutex();
await simpleMutex.protect(() => print('Simple Mutex'));

final simpleReentrantMutex = GetMutex.simpleReentrantMutex();
await simpleReentrantMutex.protect(() {
  print('Simple Reentrant Mutex');
  simpleReentrantMutex.protect(() => print('Reentrant Mutex inside'));
});

更多关于Flutter互斥锁管理插件get_mutex的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter互斥锁管理插件get_mutex的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,get_mutex 是一个用于管理互斥锁(Mutex)的插件,可以帮助你在多线程环境中安全地访问共享资源。以下是一个简单的示例,展示了如何在 Flutter 应用中使用 get_mutex 插件。

首先,你需要在 pubspec.yaml 文件中添加 get_mutex 依赖:

dependencies:
  flutter:
    sdk: flutter
  get_mutex: ^latest_version  # 请替换为实际的最新版本号

然后,运行 flutter pub get 来获取依赖。

接下来,你可以在你的 Dart 代码中导入并使用 get_mutex。以下是一个完整的示例,展示了如何使用互斥锁来管理对共享资源的访问:

import 'package:flutter/material.dart';
import 'package:get_mutex/get_mutex.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Mutex Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MutexDemoPage(),
    );
  }
}

class MutexDemoPage extends StatefulWidget {
  @override
  _MutexDemoPageState createState() => _MutexDemoPageState();
}

class _MutexDemoPageState extends State<MutexDemoPage> {
  final Mutex _mutex = Mutex();
  int _counter = 0;

  void _incrementCounter() async {
    // 尝试获取锁
    bool locked = await _mutex.tryLock();
    if (locked) {
      try {
        // 锁定后安全地修改共享资源
        setState(() {
          _counter++;
        });
        // 模拟一些耗时操作
        await Future.delayed(Duration(seconds: 1));
      } finally {
        // 确保在操作完成后释放锁
        _mutex.unlock();
      }
    } else {
      print('Failed to acquire lock');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Mutex Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

在这个示例中,我们创建了一个简单的 Flutter 应用,其中包含一个按钮和一个计数器。每次点击按钮时,都会尝试获取互斥锁 _mutex。如果成功获取锁,则安全地增加计数器的值,并在操作完成后释放锁。如果无法获取锁(例如,因为另一个操作已经持有锁),则打印一条消息。

注意,MutextryLock() 方法是尝试非阻塞地获取锁,并立即返回一个布尔值表示是否成功。如果你希望阻塞地等待锁变得可用,可以使用 lock() 方法,如下所示:

void _incrementCounter() async {
  // 尝试获取锁(阻塞等待)
  await _mutex.lock();
  try {
    // 锁定后安全地修改共享资源
    setState(() {
      _counter++;
    });
    // 模拟一些耗时操作
    await Future.delayed(Duration(seconds: 1));
  } finally {
    // 确保在操作完成后释放锁
    _mutex.unlock();
  }
}

在这个修改后的版本中,_incrementCounter 方法会阻塞等待,直到成功获取锁为止。这样可以确保在锁被释放之前,没有其他操作可以访问共享资源。不过,请注意,阻塞操作可能会导致 UI 卡顿,因此在实际应用中应谨慎使用。

回到顶部