RxJava 教程-1 简介 原理 线程控制 变换

简介

RxJava 是什么?

RxJava 在 GitHub 主页上的自我介绍是

RxJava is a Java VM implementation of ReactiveX: a library for composing asynchronous and event-based programs by using observable sequences.

RxJava是 ReactiveX 在JVM上的一个实现:一个使用可观测的序列(observable sequences)来组成(composing )异步的(asynchronous )、基于事件(event-based)的程序的库。

其实, RxJava 的本质可以压缩为异步这一个词。说到根上,它就是一个实现异步操作的库,而别的定语都是基于这之上的。

RxJava 好在哪? 一个词:简洁。 异步操作很关键的一点是程序的简洁性,因为在调度过程比较复杂的情况下,异步代码经常会既难写也难被读懂。 Android 创造的AsyncTask 和Handler ,其实都是为了让异步代码更加简洁。RxJava 的优势也是简洁,但它的简洁的与众不同之处在于,随着程序逻辑变得越来越复杂,它依然能够保持简洁。 假设有这样一个需求:界面上有一个自定义的视图,它的作用是显示多张图片,并能任意增加显示的图片。现在需要将一个给出的目录数组中每个目录下的 png 图片都加载出来并显示在View中。需要注意的是,由于读取图片的这一过程较为耗时,需要放在后台执行,而图片的显示则必须在 UI 线程执行。其中一种方式:
  1. new Thread() {
  2. @Override
  3. public void run() {
  4. super.run();
  5. for (File folder : folders) {
  6. File[] files = folder.listFiles();
  7. for (File file : files) {
  8. if (file.getName().endsWith(".png")) {
  9. final Bitmap bitmap = getBitmapFromFile(file);
  10. runOnUiThread(new Runnable() {
  11. @Override
  12. public void run() {
  13. imageCollectorView.addImage(bitmap);
  14. }
  15. });
  16. }
  17. }
  18. }
  19. }
  20. }.start();

而如果使用 RxJava ,实现方式是这样的:

  1. Observable.from(folders)
  2. .flatMap(new Func1<File, Observable<File>>() {
  3. @Override
  4. public Observable<File> call(File file) {
  5. return Observable.from(file.listFiles());
  6. }
  7. })
  8. .filter(new Func1<File, Boolean>() {
  9. @Override
  10. public Boolean call(File file) {
  11. return file.getName().endsWith(".png");
  12. }
  13. })
  14. .map(new Func1<File, Bitmap>() {
  15. @Override
  16. public Bitmap call(File file) {
  17. return getBitmapFromFile(file);
  18. }
  19. })
  20. .subscribeOn(Schedulers.io())
  21. .observeOn(AndroidSchedulers.mainThread())
  22. .subscribe(new Action1<Bitmap>() {
  23. @Override
  24. public void call(Bitmap bitmap) {
  25. imageCollectorView.addImage(bitmap);
  26. }
  27. });

观察一下你会发现, RxJava 的这个实现,是一条从上到下的链式调用,没有任何【嵌套】,这在逻辑的简洁性上是具有优势的。当需求变得复杂时,这种优势将更加明显。

另外,如果你的 IDE 是 Android Studio ,其实每次打开某个 Java 文件的时候,你会看到被自动 Lambda 化的预览,这将让你更加清晰地看到程序逻辑。

  1. Observable.from(folders)
  2. .flatMap((Func1) (folder) -> { Observable.from(file.listFiles()) })
  3. .filter((Func1) (file) -> { file.getName().endsWith(".png") })
  4. .map((Func1) (file) -> { getBitmapFromFile(file) })
  5. .subscribeOn(Schedulers.io())
  6. .observeOn(AndroidSchedulers.mainThread())
  7. .subscribe((Action1) (bitmap) -> { imageCollectorView.addImage(bitmap) });

如果你习惯使用 Retrolambda ,你也可以直接把代码写成上面这种简洁的形式。Retrolambda 是 Java 6/7 对 Lambda 表达式的非官方兼容方案,它的向后兼容性和稳定性是无法保障的,因此对于企业项目,使用 Retrolambda 是有风险的。

在Flipboard 的 Android 代码中,有一段逻辑非常复杂,包含了多次内存操作、本地文件操作和网络操作,对象分分合合,线程间相互配合相互等待,一会儿排成人字,一会儿排成一字。如果使用常规的方法来实现,肯定是要写得欲仙欲死,然而在使用 RxJava 的情况下,依然只是一条链式调用就完成了。它很长,但很清晰。

所以, RxJava 好在哪?就好在简洁,好在那把什么复杂逻辑都能穿成一条线的简洁。

原理


RxJava 的异步实现,是通过一种【扩展的观察者模式】来实现的。

RxJava 有四个基本概念:Observable (可观察者,即被观察者)、 Observer (观察者)、 subscribe (订阅)、事件。

Observable 和Observer 通过 subscribe() 方法实现订阅关系,从而 Observable 可以在需要的时候发出事件来通知 Observer。

与传统观察者模式不同, RxJava 的事件回调方法除了普通事件 onNext()之外,还定义了两个特殊的事件:onCompleted() 和 onError()。
  • onCompleted(): 事件队列完结。RxJava 不仅把每个事件单独处理,还会把它们看做一个队列。RxJava 规定,当不会再有新的onNext() 发出时,需要触发 onCompleted() 方法作为标志。
  • onError(): 事件队列异常。在事件处理过程中出异常时,onError() 会被触发,同时队列自动终止,不允许再有事件发出。
在一个正确运行的事件序列中, onCompleted() 和 onError() onCompleted() 和 onError() 二者是互斥的,有且只有一个,并且是事件序列中的最后一个。 基于以上的概念, RxJava 的基本实现主要有三点:
1、创建 观察者Observer,它决定事件触发的时候将有怎样的行为。  RxJava 中的 Observer 接口的实现方式:
  1. Observer<String> observer = new Observer<String>(){
  2. @Override
  3. public void onCompleted() {
  4. }
  5. @Override
  6. public void onError(Throwable e) {
  7. }
  8. @Override
  9. public void onNext(String s) {
  10. }
  11. };

除了 Observer 接口之外,RxJava 还内置了一个实现了 Observer 的抽象类:Subscriber。 Subscriber 对 Observer 接口进行了一些扩展,但他们的基本使用方式是完全一样的。实质上,在 RxJava 的 subscribe 过程中,Observer 也总是会先被转换成一个 Subscriber 再使用。所以如果你只想使用基本功能,选择 Observer 和 Subscriber 是完全一样的。它们的区别对于使用者来说主要有两点:

  • onStart(): 这是 Subscriber 增加的方法。它会在 subscribe 刚开始,而事件还未发送之前被调用,可以用于做一些准备工作,例如数据的清零或重置。这是一个可选方法,默认情况下它的实现为空。需要注意的是,如果对准备工作的线程有要求, onStart() 就不适用了,因为它总是在 subscribe 所发生的线程被调用,而不能指定线程。要在指定的线程来做准备工作,可以使用 doOnSubscribe() 方法。
  • unsubscribe(): 这是 Subscriber 所实现的另一个 Subscription 接口的方法,用于取消订阅。在这个方法被调用后,Subscriber 将不再接收事件。一般在这个方法调用前,可以使用 isUnsubscribed() 先判断一下状态。 unsubscribe() 这个方法很重要,因为在subscribe() 之后, Observable 会持有 Subscriber 的引用,这个引用如果不能及时被释放,将有内存泄露的风险。所以最好保持一个原则:要在不再使用的时候尽快在合适的地方(例如 onPause() onStop() 等方法中)调用 unsubscribe() 来解除引用关系,以避免内存泄露的发生。

2、创建 被观察者Observable,它决定什么时候触发事件以及触发怎样的事件。

RxJava 使用 create() 方法来创建一个 Observable ,并为它定义事件触发规则:

Observable observable = Observable.create(new Observable.OnSubscribe<String>() {

@Overridepublic

void call(Subscriber<? super String> subscriber) {

subscriber.onNext("Hello");

subscriber.onNext("Hi");

subscriber.onNext("Aloha");

subscriber.onCompleted();

}

});

可以看到,这里传入了一个 OnSubscribe 对象作为参数。OnSubscribe 会被存储在返回的 Observable 对象中,它的作用相当于一个计划表,当 Observable 被订阅的时候,OnSubscribe 的 call() 方法会自动被调用,事件序列就会依照设定依次触发。这样,由被观察者调用了观察者的回调方法,就实现了由被观察者向观察者的事件传递,即观察者模式。

create() 方法是 RxJava 最基本的创造事件序列的方法。基于这个方法, RxJava 还提供了一些方法用来快捷创建事件队列,如上面的示例等价于

Observable observable = Observable.just("Hello", "Hi", "Aloha");

Observable observable = Observable.from(new String[]{"Hello", "Hi", "Aloha"});

  • just(T...):将传入的任意参数依次发送出来
  • from(T[]) / from(Iterable<? extends T>):将传入的数组或 Iterable 拆分成具体对象后依次发送出来

3、使用订阅方法subscribe()将观察者和被观察者联结起来

observable.subscribe(observer或subscriber);

有人可能会注意到, subscribe() 这个方法有点怪:它看起来是『被观察者observalbe 订阅了 观察者observer / subscriber』而不是『observer /subscriber 订阅了 observalbe』,这看起来就像『杂志订阅了读者』一样颠倒了对象关系。这让人读起来有点别扭,不过如果按照那种设计虽然更加符合思维逻辑,但对流式 API 的设计就造成影响了,比较起来明显是得不偿失的。

Observable.subscribe(Subscriber) 的内部实现是这样的(仅核心代码):

  1.     public Subscription subscribe(Subscriber subscriber) {
  2.         subscriber.onStart();//可选的准备方法
  3.         onSubscribe.call(subscriber);//事件发送的逻辑开始运行
  4.         return subscriber;//将传入的 Subscriber 作为 Subscription 返回,这是为了方便 unsubscribe()
  5.     }

从这也可以看出,在 RxJava 中,Observable 并不是在创建的时候就立即开始发送事件的,而是在它被订阅的时候,即当 subscribe() 方法执行的时候才开始发送


不完整定义回调

除了 subscribe(Observer或Subscriber) ,subscribe() 还支持不完整定义的回调,RxJava 会自动根据定义创建出Subscriber 。形式如下:

  1. Action1<String> onNextAction = new Action1<String>() {
  2. public void call(String s) { // onNext()
  3. }
  4. };
  5. Action1<Throwable> onErrorAction = new Action1<Throwable>() {
  6. public void call(Throwable throwable) { // onError()
  7. }
  8. };
  9. Action0 onCompletedAction = new Action0() {
  10. @Override
  11. public void call() {// onCompleted()
  12. }
  13. };
  14. observable.subscribe(onNextAction);// 自动创建 Subscriber,并使用 onNextAction 来定义 onNext()
  15. observable.subscribe(onNextAction, onErrorAction);
  16. observable.subscribe(onNextAction, onErrorAction, onCompletedAction);

这段代码中出现的Action0 是 RxJava 中定义的一个接口,它只有一个方法 call(),这个方法是无参无返回值的;由于 observer或subscriber的 onCompleted() 方法也是无参无返回值的,因此 Action0 可以被当成一个包装对象,将 onCompleted() 的内容打包起来将自己作为一个参数传入 subscribe() 以实现不完整定义的回调。这样其实也可以看做将 onCompleted() 方法作为参数传进了subscribe(),相当于其他某些语言中的『闭包』。

Action1 也是一个接口,它同样只有一个方法 call(T param),这个方法也无返回值,但有一个参数;与 Action0 同理,由于 onNext(T obj) 和 onError(Throwable error) 也是单参数无返回值的,因此 Action1 可以将onNext(obj) 和 onError(error) 打包起来传入 subscribe() 以实现不完整定义的回调。

事实上,虽然 Action0 和 Action1 在 API 中使用最广泛,但 RxJava 是提供了多个 ActionX 形式的接口 (例如 Action2, Action3) 的,它们可以被用以包装不同的无返回值的方法。


场景示例

将字符串数组中的所有字符串依次打印出来:

  1. Observable.just("包青天", "白乾涛", "baiqiantao").subscribe(new Observer<String>() {
  2. public void onNext(String name) {
  3. System.out.println(name);
  4. }
  5. public void onCompleted() {
  6. }
  7. public void onError(Throwable e) {
  8. }
  9. });
  10. Observable.just("包青天", "白乾涛", "baiqiantao").subscribe(new Action1<String>() {
  11. @Override
  12. public void call(String name) {System.out.println(name);}
  13. });
  14. Observable.just("包青天", "白乾涛", "baiqiantao").subscribe(name -> {System.out.println(name);});

线程控制 Scheduler

在 RxJava 的默认规则中,事件的发出和消费都是在同一个线程的。也就是说,如果只用上面的方法,实现出来的只是一个同步的观察者模式。观察者模式本身的目的就是『后台处理,前台回调』的异步机制,因此异步对于 RxJava 是至关重要的。而要实现异步,则需要用到 RxJava 的另一个概念: Scheduler (调度程序,日程安排程序)。

Scheduler 的 API 

在RxJava 中,Scheduler(调度器)相当于线程控制器,RxJava 通过它来指定每一段代码应该运行在什么样的线程。RxJava 已经内置了几个 Scheduler ,它们已经适合大多数的使用场景:
  • Schedulers.immediate():直接在当前线程运行,相当于不指定线程,这是默认的 Scheduler。
  • Schedulers.newThread():总是启用新线程,并在新线程执行操作。
  • Schedulers.io():I/O 操作(读写文件、读写数据库、网络信息交互等)所使用的 Scheduler。行为模式和 newThread() 差不多,区别在于 io() 的内部实现是是用一个无数量上限的线程池,可以重用空闲的线程,因此多数情况下 io() 比 newThread() 更有效率。不要把计算工作放在 io() 中,可以避免创建不必要的线程。
  • Schedulers.computation():计算所使用的 Scheduler。这个计算指的是 CPU 密集型计算,即不会被 I/O 等操作限制性能的操作,例如图形的计算。这个 Scheduler 使用的固定的线程池,大小为 CPU 核数。不要把 I/O 操作放在 computation() 中,否则 I/O 操作的等待时间会浪费 CPU。
  • AndroidSchedulers.mainThread(),是Android 专用的Scheduler,它指定的操作将在 Android 主线程运行。
有了这几个 Scheduler ,就可以使用 subscribeOn() 或 observeOn() 两个方法来对线程进行控制了。
  • subscribeOn():指定subscribe() 所发生的线程,即 Observable.OnSubscribe 被激活时所处的线程,或者叫做事件产生的线程。
  • observeOn():指定Subscriber 所运行在的线程,或者叫做事件消费的线程。

示例
  1. Observable.just(1, 2, 3, 4).subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程
  2. .observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程
  3. .subscribe(number -> {Log.i("bqt", "number:" + number); });

事实上,这种在 subscribe() 之前写上两句 subscribeOn(Scheduler.io()) 和 observeOn(AndroidSchedulers.mainThread()) 的使用方式非常常见,它适用于多数的 『后台线程取数据,主线程显示』的程序策略。

变换


终于要到牛逼的地方了,不管你激动不激动,反正我是激动了。

RxJava 提供了对事件序列进行变换的支持,这是它的核心功能之一,也是大多数人说『RxJava 真是太好用了』的最大原因。

所谓变换,就是将事件序列中的对象或整个序列进行加工处理,转换成不同的事件或事件序列。

1、map() 首先看一个 map() 的例子。
  1. public class Bean {
  2. public String name;
  3. public int age;
  4. public List<String> courses = Arrays.asList("语文", "数学", "英语");
  5. public Bean() {
  6. }
  7. public Bean(String name, int age) {
  8. this.name = name;
  9. this.age = age;
  10. }
  11. @Override
  12. public String toString() {
  13. return "name=" + name + " age=" + age;
  14. }
  15. }

打印一组学生的名字

  1. //打印出一组学生的名字
  2. Bean[] beans = {new Bean("包青天", 27), new Bean("白乾涛", 26)};
  3. Observable.from(beans)//
  4. .map(new Func1<Bean, String>() {
  5. @Override
  6. public String call(Bean bean) { //输入类型为Bean,返回类型为String
  7. return bean.name;
  8. }
  9. })
  10. .subscribe(name -> {Log.i("bqt", name);});//输入类型变成了为String

这里出现了一个叫做 Func1 的类,它和 Action1 非常相似,也是 RxJava 的一个接口,用于包装含有一个参数的方法。

和 ActionX 一样, FuncX 也有多个,用于不同参数个数的方法,FuncX 和ActionX 的区别在 FuncX 包装的是【有返回值】的方法。

使用Lambda简化上述代码

  1. //打印出一组学生的名字
  2. Bean[] beans = {new Bean("包青天", 27), new Bean("白乾涛", 26)};
  3. Observable.from(beans)//
  4. .map(bean -> {return bean.name;})// 输入类型为 bean,返回类型为String
  5. .subscribe(name -> {Log.i("bqt", name);});

可以看到,map() 方法将参数中的Bean 对象转换成一个 String 对象后返回,而在经过 map() 方法后,事件的参数类型也由 Bean转为了 String 。

这种直接变换对象并返回的,是最常见的也最容易理解的变换。不过 RxJava 的变换远不止这样,它不仅可以针对事件对象,还可以针对整个事件队列,这使得 RxJava 变得非常灵活。

map() 示意图:


2、flatMap()

这是一个很有用但非常难理解的变换。

还是上面的需求,如果我要打印出每个学生所需要修的所有课程的名称(每个学生只有一个名字,但却有多个课程),该怎么做呢?

按照以前的思维,我们可以这样:

  1. Bean[] beans = {new Bean("包青天", 27), new Bean("白乾涛", 26)};
  2. Observable.from(beans)//
  3. .subscribe(bean -> {
  4. List<String> courses = bean.courses;
  5. for (int i = 0 ; i < courses.size() ; i++) {
  6. Log.i("bqt", courses.get(i));
  7. }
  8. });

依然很简单。那么如果我不想在 Subscriber 中使用 for 循环,而是希望 Subscriber 中直接传入单个的课程(String)对象呢(这对于代码复用很重要)?

用 map() 显然是不行的,因为 map() 是一对一的转化,而我现在的要求是一对多的转化。那怎么才能把一个 Student 转化成多个 String呢?

这个时候,就需要用 flatMap() 了:

  1. Bean[] beans = {new Bean("包青天", 27), new Bean("白乾涛", 26)};
  2. Observable.from(beans)//
  3. .flatMap(new Func1<Bean, Observable<String>>() {
  4. @Override
  5. public Observable<String> call(Bean bean) {
  6. return Observable.from(bean.courses);
  7. }
  8. })
  9. .subscribe(name -> {Log.i("bqt", name);});

flatMap() 和 map() 有一个相同点:它也是把传入的参数转化之后返回另一个对象。但需要注意,和map() 不同的是, flatMap() 中返回的是一个 Observable 对象,但是这个 Observable 对象并不是被直接发送到了 Subscriber 的回调方法中(意思是说,subscribe方法接受的参数不是创建的这个Observable 对象,而是这个Observable 对象发送的事件)。

flatMap() 的原理是这样的:

  • 1. 使用传入的事件对象创建一个 Observable 对象;
  • 2. 并不发送这个 Observable,而是将它激活,于是它开始发送事件;
  • 3. 每一个创建出来的 Observable 发送的事件,都被汇入同一个 Observable,而这个 Observable 负责将这些事件统一交给Subscriber 的回调方法。

这三个步骤,把事件拆成了两级,通过一组新创建的 Observable 将初始的对象『铺平』之后通过统一路径分发了下去。而这个『铺平』就是 flatMap() 所谓的 flat。

flatMap() 示意图:

包青天解释:

  • 1、最开始的Observable在被订阅后开始发送事件,此时发送的事件为两个圆形(比如上例中的Bean)
  • 2、在被flatMap后,每个圆形都生成了一个对应的Observable 对象,但是并不是直接把这两个Observable发送到Observer中
  • 3、而是将这两个Observable对象激活,于是这两个Observable 对象就开始发送事件了
  • 4、这两个Observable 对象各自发送了两个事件,此时发送的事件类型都为方形(比如上例中的String)
  • 5、这两个Observable 对象一共发送了四个事件,这四个事件又都被汇入到同一个Observable中(就像图中的那块云)
  • 6、最后由这个Observable对象负责将这四个事件发送给Observer中

来自为知笔记(Wiz)

时间: 2024-10-27 01:11:16

RxJava 教程-1 简介 原理 线程控制 变换的相关文章

RxJava使用(三)Scheduler 线程控制

前言 在默认情况下,即在不指定线程的情况下,RxJava 遵循的是线程不变的原则,即:在哪个线程调用 subscribe(),就在哪个线程生产事件:在哪个线程生产事件,就在哪个线程消费事件. 如果需要切换线程,就需要用到 Scheduler (调度器). Schedulers部分主要来自<给Android 开发者的 RxJava 详解> Schedulers介绍 在RxJava 中,Scheduler --调度器,相当于线程控制器,RxJava 通过它来指定每一段代码应该运行在什么样的线程.R

RX系列五 | Schedulers线程控制

RX系列五 | Schedulers线程控制 在我们上一篇文章中的,我们的小例子里有这么一段代码 //网络访问 .observeOn(Schedulers.io()) 事实上,我们在使用网络操作的时候,便可以控制其运行在哪个线程中,而Schedulers类,有四个方法,分别是 Schedulers.immediate(); Schedulers.newthread(); Schedulers.io(); Schedulers.computation(); 以及RxAndroid中的Android

Linux之线程、线程控制、线程属性

一.整体大纲 二.线程相关 1. 什么是线程    LWP:light weight process 轻量级的进程,本质仍是进程(在Linux环境下) 进程:独立地址空间,拥有PCB 线程:也有PCB,但没有独立的地址空间(共享) 区别:在于是否共享地址空间. 独居(进程):合租(线程). Linux下: 线程:最小的执行单位 进程:最小分配资源单位,可看成是只有一个线程的进程. 2. Linux内核线程实现原理     (1)线程实现原理 类Unix系统中,早期是没有“线程”概念的,80年代才

线程控制与分离

线程: 在一个进程的地址空间中执行多个线程 ---- 强调共享  线程是进程中的一个实体. 线程私有: 线程id 上下文信息 (包括各种寄存器的值.程序计数器和栈指针) (私有)栈空间    errno变量    信号屏蔽字    调度优先级 此时:POSIX标准   编译时  加上 -lpthread 线程与进程的区别: 1.进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位: 线程是进程的一个实体. 2.进程 强调 独占,线程 强调 共享

线程基础--线程控制

3.  线程控制 1). 线程属性 目标:可以设置 线程的 detached/join 状态,线程栈的大小和最低地址等属性. detached/join 状态的区别: 当线程处于 分离状态(detached)时,线程结束时,os立即回收资源.主线程不可以调用pthread_join获取线程退出时的返回值. 当线程处于 未分离状态(join)时,线程结束时,主线程 调用pthread_join获取线程退出时的返回值, 随后释放该线程资源. a)数据类型 pthread_attr_t b)初始化及释

Linux/UNIX线程控制

线程控制 线程属性 调用pthread_create函数的例子中,传入的参数都是空指针,而不是指向pthread_attr_t结果的指针.可以用pthread_attr_t结构修改线程默认属性,并把这些属性与创建的线程联系起来.可以使用pthread_attr_init函数初始化pthreaad_attr_t结构.调用pthread_attr_init以后,pthread_attr_t结构所包含的内容就是操作系统实现支持的线程所有属性的默认值.如果要修改其中个别属性的值,需要调用其他的函数.pt

struts2官方 中文教程 系列十二:控制标签

介绍 struts2有一些控制语句的标签,本教程中我们将讨论如何使用 if 和iterator 标签.更多的控制标签可以参见 tags reference. 到此我们新建一个struts2 web 项目:struts_basic2 本帖地址:struts2官方 中文教程 系列十二:控制标签 即 http://www.cnblogs.com/linghaoxinpian/p/6941683.html 下载本章节代码 struts2 if标签 我们在thankyou.jsp中添加如下代码: <s:i

linux线程控制&amp;线程分离

线程概念 线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元. 线程是程序中一个单一的顺序控制流程.进程内一个相对独立的.可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位.在单个程序中同时运行多个线程完成不同的工作,称为多线程. 线程资源 由于一个程序中的多个线程共享同一地址空间,因此代码段,数据段内容是共享的.除此之外,以下内容也是共享的: 1. 文件描述符表2. 每种信号的处理方式(SIG_IGN.SIG_DFL

Windows线程控制

多线程无疑带来了很多方便,提高了很多开发效率,但是同时也带来了很多问题. 举个栗子: DWORD WINAPI ThreadProc(LPVOID lPParameter); int m = 0; int n = 0; int main(int argc, TCHAR* argv[], TCHAR* envp[]) { HANDLE hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadProc,NULL,0,NULL); int