Flutter信号量控制插件semaphore_plus的使用

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

Flutter信号量控制插件semaphore_plus的使用

semaphore_plus简介

pub package

semaphore_plus是一个轻量级的数据类型,用于在隔离区内控制对公共资源的协作访问。这个包是原semaphore包的延续,由于原作者个人原因,该包被标记为已停止维护。

使用示例

示例1:Semaphore基础用法

下面的代码展示了如何使用LocalSemaphore来限制同时执行的任务数量。在这个例子中,我们设置最大并发任务数为3,然后启动9个异步任务,每个任务会尝试获取信号量许可,并模拟一些工作后释放许可。

import 'dart:async';
import 'package:semaphore_plus/semaphore_plus.dart';

Future<void> main(List<String> args) async {
  final maxCount = 3;
  final running = <int>[];
  var simultaneous = 0;
  final sm = LocalSemaphore(maxCount);
  final tasks = <Future>[];

  for (var i = 0; i < 9; i++) {
    tasks.add(Future(() async {
      try {
        await sm.acquire(); // 获取信号量
        running.add(i);
        if (simultaneous < running.length) {
          simultaneous = running.length;
        }

        print('Start $i, running $running');
        await _doWork(100); // 模拟工作
        running.remove(i);
        print('End   $i, running $running');
      } finally {
        sm.release(); // 释放信号量
      }
    }));
  }

  await Future.wait(tasks);
  print('Max permits: $maxCount, max simultaneous runned: $simultaneous');
}

Future _doWork(int ms) {
  return Future.delayed(Duration(milliseconds: ms));
}

输出结果:

Start 0, running [0]
Start 1, running [0, 1]
Start 2, running [0, 1, 2]
End   0, running [1, 2]
Start 3, running [1, 2, 3]
End   1, running [2, 3]
Start 4, running [2, 3, 4]
...
Max permits: 3, max simultaneous runned: 3

从输出可以看到,在任何时候最多只有三个任务在运行,当一个任务结束时,另一个等待中的任务就会开始执行,确保了并发度不超过设定的最大值。

示例2:条件变量与锁的结合使用

除了基本的信号量功能外,semaphore_plus还提供了更复杂的同步机制如条件变量(Condition Variable)和锁(Lock)。这些工具可以用来实现生产者-消费者模式等高级场景。以下是基于条件变量的一个简单生产者-消费者模型的例子:

import 'dart:async';
import 'dart:collection';
import 'dart:math';
import 'package:semaphore_plus/lock.dart';
import 'package:semaphore_plus/condition_variable.dart';

final _cvEmpty = ConditionVariable(_lock);
final _cvFull = ConditionVariable(_lock);
final _lock = Lock();
final _queue = Queue<int>();
var counter = 0;

Future<void> _doWork(int max) async {
  final milliseconds = Random().nextInt(max);
  await Future.delayed(Duration(milliseconds: milliseconds));
}

Future<void> _producer(String id) async {
  while (true) {
    await lock(_lock, () async {
      while (_queue.length >= 2) {
        print('producer $id: wait $_queue');
        await _cvFull.wait();
      }

      print('producer $id: $counter');
      await _doWork(1000);
      _queue.add(counter++);
      await _cvEmpty.signal();
    });
  }
}

Future<void> _consumer(String id) async {
  while (true) {
    int number;
    await lock(_lock, () async {
      while (_queue.isEmpty) {
        print('consumer $id: wait $_queue');
        await _cvEmpty.wait();
      }

      number = _queue.removeFirst();
      await _cvFull.signal();
    });

    print('consumer $id: $number');
    await _doWork(1000);
    print(number);
  }
}

Future<void> main() async {
  await Future.wait([
    _producer('one'),
    _producer('two'),    
    _consumer('one'),
    _consumer('two'),
    _consumer('three'),
  ]);
}

此段代码实现了两个生产者不断向队列添加数据,而三个消费者则从队列中取出数据处理。通过条件变量_cvEmpty_cvFull以及锁_lock来协调生产和消费过程中的同步问题,避免了竞争条件的发生。

以上就是关于semaphore_plus插件的基本介绍及使用方法,希望对你有所帮助!如果有任何疑问或需要进一步的帮助,请随时提问。


更多关于Flutter信号量控制插件semaphore_plus的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter信号量控制插件semaphore_plus的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter中使用semaphore_plus插件进行信号量控制的代码示例。semaphore_plus是一个Flutter插件,它提供了对信号量(Semaphore)的支持,信号量是一种用于控制对共享资源的访问的同步机制。

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

dependencies:
  flutter:
    sdk: flutter
  semaphore_plus: ^x.y.z  # 请替换为最新版本号

然后运行flutter pub get来安装依赖。

接下来,这里有一个简单的例子,展示了如何使用semaphore_plus来控制对某个资源的并发访问:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Semaphore Plus Example'),
        ),
        body: Center(
          child: SemaphoreExample(),
        ),
      ),
    );
  }
}

class SemaphoreExample extends StatefulWidget {
  @override
  _SemaphoreExampleState createState() => _SemaphoreExampleState();
}

class _SemaphoreExampleState extends State<SemaphoreExample> {
  final Semaphore _semaphore = Semaphore(2); // 初始化一个信号量,最大许可数为2
  int _counter = 0;

  void _incrementCounter() async {
    // 尝试获取一个许可
    final Permit? permit = await _semaphore.acquire();
    if (permit != null) {
      try {
        // 模拟对共享资源的访问
        await Future.delayed(Duration(seconds: 1));
        setState(() {
          _counter++;
        });
      } finally {
        // 释放许可
        permit.release();
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Text(
          'You have pushed the button this many times:',
        ),
        Text(
          '$_counter',
          style: Theme.of(context).textTheme.headline4,
        ),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: () {
            for (int i = 0; i < 5; i++) {
              // 模拟快速点击按钮,触发多次_incrementCounter调用
              _incrementCounter();
            }
          },
          child: Text('Increment'),
        ),
      ],
    );
  }
}

在这个例子中,我们创建了一个信号量_semaphore,其最大许可数为2。这意味着在任何时候,最多只能有两个任务可以同时执行_incrementCounter函数中的代码块。

当用户点击按钮时,我们尝试获取一个许可。如果成功获取到许可,我们就模拟对共享资源的访问(这里使用Future.delayed来模拟耗时操作),然后更新计数器。无论操作是否成功,我们都会在finally块中释放许可,以确保许可被正确归还。

由于我们同时触发了5次_incrementCounter调用,但由于信号量的限制,最多只能有两个调用同时执行。其他调用将被阻塞,直到有许可被释放。

这个例子展示了如何使用semaphore_plus插件来控制对共享资源的并发访问,以避免资源竞争和数据不一致的问题。

回到顶部