Flutter

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

2020-09-27  本文已影响0人  tp夕阳武士

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 是什么东西?

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是什么意思?

如果在开发中,有很多的的耗时计算,开发者完全可以根据自己的需求创建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需要带参数调用方法,并且还想对它的计算进行监听的话,应该怎么实现?

上一篇下一篇

猜你喜欢

热点阅读