美文网首页Flutter
Flutter-非阻塞调用操作与多线程

Flutter-非阻塞调用操作与多线程

作者: tp夕阳武士 | 来源:发表于2020-09-27 17:00 被阅读0次

Dart 代码运行在单个执行“线程”中。如果 Dart 代码在执行时阻塞,例如:处理一个需要长时间运行的计算操作或等待 I/O 完成。此时整个程序会被“冻结”。

异步操作可以让你的程序在等待一个操作完成时继续处理其它的工作。Dart 使用 Future 对象来表示异步操作的结果。你可以用 async 和 await 关键字或 Future 类的相关 API 来配合使用 future。

1.Future

下面是一段会产生线程堵塞的代码

import 'dart:io';

main(List<String> args) {
  print('main start ${getDateStringNow()}');

  print('${gatherNewsReports()}');

  print('main start ${getDateStringNow()}');
}

//一个线程堵塞事件
String gatherNewsReports() {
  //睡眠3秒钟
  sleep(Duration(seconds: 3));
  //获得时间
  String dateStr = getDateStringNow();
  //返回时间;
  return 'this is news reports $dateStr';
}

//获取当前的时间
String getDateStringNow() {
  int timeStamp = DateTime.now().millisecondsSinceEpoch;

  String dateStr = getDateStringFromTimeStamp(timeStamp: timeStamp);

  return dateStr;
}

//根据时间戳/年月日格式获取时间
String getDateStringFromTimeStamp({
  int timeStamp = 0, //fromMillisecondsSinceEpoch
  String yyyy = '年',
  String MM = '月',
  String dd = '日',
  String hh = ':',
  String mm = ':',
}) {
  if (timeStamp == null || timeStamp == "") {
    return "";
  }

  //把时间戳转化成DateTime实例对象;
  // DateTime theDate = DateTime.fromMicrosecondsSinceEpoch(timeStamp * 1000);
  DateTime theDate = DateTime.fromMillisecondsSinceEpoch(timeStamp);

  var year = theDate.year.toString(); //取的年份String类型
  var month = theDate.month.toString(); // 取的月份,String类型
  if (theDate.month <= 9) {
    month = "0" + month;
  }
  var day = theDate.day.toString(); // 取的日期,String类型;
  if (theDate.day < 10) {
    day = "0" + day;
  }

  var hour = theDate.hour.toString(); // 取的小时,String类型;
  if (theDate.hour < 10) {
    hour = '0' + hour;
  }

  var minutes = theDate.minute.toString(); // 取的分钟数,String类型;
  if (theDate.minute < 10) {
    minutes = '0' + minutes;
  }

  var seconds = theDate.second.toString(); // 取的秒数,String类型;
  if (theDate.second < 10) {
    seconds = '0' + seconds;
  }

  //声明一个空时间字符串用来存储最后的时间;
  String finalDateString = '';

  finalDateString = '$year$yyyy$month$MM$day$dd$hour$hh$minutes$mm$seconds';

  return finalDateString;
}

打印结果:

main start 2020年09月25日14:21:26
this is news reports 2020年09月25日14:21:29
main start 2020年09月25日14:21:29

gatherNewsReports();事件堵塞了线程3秒钟;

使用Future对阻塞线程的事件进行异步调用的方案:

import 'dart:io';

main(List<String> args) {
  print('main start ${getDateStringNow()}');

  // print('${gatherNewsReports()}');

  gatherNewsReports().then((value) {
    //成功获得回调
    print(value);
  }).catchError((e) {
    print(e.runtimeType);
  });

  print('main start ${getDateStringNow()}');
}

//一个线程堵塞事件
Future<String> gatherNewsReports() {
  Future f = Future<String>(() {
    //睡眠3秒钟
    sleep(Duration(seconds: 3));
    //获得时间
    String dateStr = getDateStringNow();
    //返回时间;
    if (true) {
      return 'this is news reports $dateStr';
    } else {
      var resulet = ['this is news reports $dateStr'];
      throw Exception(resulet);
    }
  });

  return f;
}

打印结果:

main start 2020年09月25日14:26:58
main start 2020年09月25日14:26:58
this is news reports 2020年09月25日14:27:01

代码分析:
在上面的代码中,创建了一个Future是里f,把耗时的操作放在f的的回调方法中了,所以线程不会被耗时操作堵塞,并且把Future的实例作为了函数的返回结果,当耗时操作结束后,得到的结果会通过f的实例方法then(value);得到函数耗时操作的结果;
所以在main()函数中,调用gatherNewsReports()方法后,可以通过Future的实例的then(onvalue);方法拿到函数的回调结果;
当函数执行有异常需要抛出的时候,可以通过throw语法抛出异常,通过FuturecatchError();方法处理异常回调;更多细节可以查看Flutter官方文档;

1.1 Future的嵌套使用
void FutureTest2() {
  print('strat ${getDateStringNow()}');
  Future<List>(() {
    sleep(Duration(seconds: 3));
    return ['AAA ${getDateStringNow()}'];
  }).then((List value) {
    //第一次操作回调结果 value1
    print('value1 = $value');

    //拿到第一次结果后,进行第二次操作:
    Future(() {
      sleep(Duration(seconds: 3));
      String obj = 'BBB ${getDateStringNow()}';
      value.add(obj);
      return value;
    }).then((value) {
      //拿到第二次操作的回调结果;
      print('value2 = $value');
    });
  });
  print('end ${getDateStringNow()}');
}
1.2 Future嵌套的链式调用
//Future的嵌套使用2
void FutureTest3() {
  print('strat ${getDateStringNow()}');
  Future<List>(() {
    //第一次异步操作
    sleep(Duration(seconds: 3));
    return ['AAA ${getDateStringNow()}'];
  }).then((List value) {
    //得到第一次异步操作的回调,并且执行第二次异步操作
    print(value);
    sleep(Duration(seconds: 3));
    value.add('BBB ${getDateStringNow()}');
    return value;
  }).then((value) {
    //得到第二次异步操作的回调
    print(value);
  });
  print('end ${getDateStringNow()}');
}
1.3 Future 的其他使用方式:
void FutureTest4() {
  //成功回调的链式语法
  Future.value('what the fuck!').then((value) => print(value));

  Map<String, int> error = {'value': 1};
  //异常抛出
  Future<Map<String, int>>.error(error).catchError((er) {
    print(er.runtimeType);
    var result = er['value'];
    print(result);
  });

  //延迟执行
  Future.delayed(Duration(seconds: 3), () {
    return 'delayed Future';
  }).then((value) => print(value));
}
main(List<String> args) {
  
  //5.Future的带参写法
  getNewSting('aaa').then((value) {
    print(value);
    return getNewSting(value);
  }).then((value) {
    print(value);
    return getNewSting(value);
  }).then((value) {
    print(value);
  });
}

int i = 0;
Future getNewSting(String msg) {
  i++;
  msg = '${msg}-$i';
  return Future.value(msg);
}


//写法2:
main(List<String> args) async {
  var f1 = await getNewSting('hello flutter');
  print(f1);
  var f2 = await getNewSting(f1);
  print(f2);
  var f3 = await getNewSting(f2);
  print(f3);
  var f4 = getNewSting(f3);

  print(f4.runtimeType);
}

Future getNewSting(String msg) {
  return Future(() {
    sleep(Duration(seconds: 3));
    return '${msg} + hello flutter';
  });
}
//优化写法

2.await & async

await , async 是什么东西?

  • 它们是Dart语法中 关键字,所以在写代码的时候,是不能使用这种变量名的
  • 使用 await , async 可以实现使用同步代码的格式去实现异步调用的效果
  • 并且通过一个async函数可以返回一个Futureinstance
2.1 案例代码演练
import 'dart:io';
import 'lib/rf_timetool.dart';

main(List<String> args) {
  print('main start ${getDateStringNow()}');
  getNetworkData().then((value) => print(value));
  print('main end ${getDateStringNow()}');
}



/*
await 和 async 的使用需要解决的两个问题
1. await 必须在 async 函数中调用
2. async 函数返回的结果必须是Future;

*/
Future<String> getNetworkData() async {
  await sumMethod();
  return 'you have get datas from net ${getDateStringNow()}';
}
//这个地方还有一个点需要注意
// async 函数只能返回 Future,但是我们可以看到上面这个函数字面上返回一个String
//其实dart 语法运行时会自动帮我们把String包装成 Future的retuan value


void sumMethod() {
  int a = 0;
  for (var i = 0; i < 100; i++) {
    a += i;
  }
  print('$a ${getDateStringNow()}');
}

3.多核CPU的利用

3.1.Isolate的概念

Dart语法中有一个Isolate的概念,Isolate是什么意思?

  • 我们已经知道Dart是单线程的,这个线程有自己可以访问的内存空间,以及需要运行的事件循环;
  • 我们可以将这个空间系统称作为Isolate;
  • 比如Flutter中就有一个Root Isolate,负责运行Flutter代码,比如UI渲染和用户交互等;
  • 在Isolate中资源隔离做的非常好.每个Isolate都有自己的Even roop 和 Queue
  • Isolate 之间不共享任何的资源,只依靠消息机制通讯,因此就没有资源抢占的问题,
  • 但是如果只有一个Isolate的话,意味着永远只能利用一个线程,对于多个的设备来说就是一种资源浪费.

如果在开发中,有很多的的耗时计算,开发者完全可以根据自己的需求创建Isolate,在独立的Isolate中完成,计算操作.

3.1.1如何创建Isolate

创建Isolate比较简单:

import 'dart:io';
import 'dart:isolate';
import 'lib/rf_timetool.dart';

main(List<String> args) {
  print('main start ${DateTime.now()}');

  //开辟另一个Isolate执行耗时操作
  Isolate.spawn(caculate, 100).then((value) {
    print('value = $value');
    print('${getDateStringNow()}');
  });

  print('main end ${DateTime.now()}');
}

caculate(int count) {
  sleep(Duration(seconds: 3));
  // print('${getDateStringNow()}');
  var total = 0;
  for (var i = 0; i <= count; i++) {
    total += i;
  }
  print(total);
  return total;
}

Isolate之间的通讯:

import 'dart:io';
import 'dart:isolate';

main(List<String> args) async {
  print('main start ${DateTime.now()}');

  //1. 创建管道
  ReceivePort port = ReceivePort();

  //2.创建Isolata
  Isolate isolate = await Isolate.spawn(foo, port.sendPort);

  //3.监听管道
  port.listen((message) {
    print('$message , ${DateTime.now()}');
    //关闭监听;
    port.close();
    // 销毁被监听的Isolate
    isolate.kill();
  });

  print('main end ${DateTime.now()}');
}

foo(SendPort senport) {
  sleep(Duration(seconds: 3));
  int total = 0;
  for (var i = 0; i < 100000000; i++) {
    total += i;
  }
  return senport.send('total = ${total}');
}

问题:如果Isolate需要带参数调用方法,并且还想对它的计算进行监听的话,应该怎么实现?

相关文章

  • Flutter-非阻塞调用操作与多线程

    Dart 代码运行在单个执行“线程”中。如果 Dart 代码在执行时阻塞,例如:处理一个需要长时间运行的计算操作或...

  • IO多路复用 2021-10-03

    阻塞和非阻塞 进程进入waiting状态,就是阻塞阻塞/非阻塞,关注的是调用方的状态,往往是与系统调用(syste...

  • 阻塞与非阻塞区别

    2. 阻塞与非阻塞 阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态. 阻塞调用是指调用结果返回之前...

  • 《后台开发 核心技术与应用实战》--网络IO模型

    阻塞和非阻塞的概念描述的是用户线程调用内核IO操作的方式:阻塞:指IO操作彻底完成后才返回到用户空间非阻塞:指IO...

  • dubbo初了解 异步调用

    异步调用 基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较...

  • 经常被问的典型面试问题

    1. 阻塞IO与非阻塞IO, 同步和异步IO各自是什么? 阻塞IO,IO操作被用户调用的时候不会立即产生结果。而是...

  • 单核,多核,单线程,多线程,同步,异步的理论概念

    单核多核单线程多线程同步异步原子操作非原子操作并发串行分布式计算阻塞非阻塞 1、首先明确一点,对于单核CPU,任意...

  • 非阻塞connect和accept

    1 非阻塞connect 1.1 阻塞模式与非阻塞 调用connect会发起三次连接。 阻塞模式下,connect...

  • 非阻塞I/O

    阻塞 VS 非阻塞 当应用程序调用阻塞 I/O 完成某个操作时,应用程序会被挂起,等待内核完成操作,感觉上应用程序...

  • 同步与非同步、阻塞与非阻塞

    同步与异步、阻塞与非阻塞 同步:一个进程(或者线程)在操作 I/O 时,必须要自己去调用方法查看是否数据已经准备好...

网友评论

    本文标题:Flutter-非阻塞调用操作与多线程

    本文链接:https://www.haomeiwen.com/subject/whqzyktx.html