美文网首页
对IO操作的正确方式

对IO操作的正确方式

作者: Little_Mango | 来源:发表于2018-08-10 22:16 被阅读15次

对本地文件进行操作的时候我们需要注意多线程安全,最安全和最高效的方法是在同一时刻支持多读单写,也就是同时可以并发读取操作,但是写操作是同步的,在多线程中有个名词叫做栅栏,iOS 和 Android 都有相应的 API,我们可以把栅栏看成是一个串行的队列,所以栅栏是同步一个紧挨着一个执行的,这样就可以确保我们对数据库的操作是串行的,那么就不会出现数据混乱,同时在每一个栅栏里面的区间的操作是并发的,举个例子:

在时间线上我们需要对数据库进行以下操作 写写读读写读写

如果我们用栅栏的方式来做,那么就是创建一个队列,然后划分成几个栅栏,这里是写|写|读读|写|读|写。我们可以看到其中有一个区间是|读读|,因为读操作是不会导致数据错乱的,所以在时间线上,只要先执行了写|写|操作后,并发读操作和同步读操作得到的结果是一样的,为了提高效率我们在这个时候可以并发操作,这就是栅栏。
在 iOS上的 API 如下:

void dispatch_barrier_sync(dispatch_queue_t queue,
        DISPATCH_NOESCAPE dispatch_block_t block);

void dispatch_barrier_async(dispatch_queue_t queue,
        DISPATCH_NOESCAPE dispatch_block_t block);

在 Java 中是使用CyclicBarrier.java(我还没用过,看了一下应该是它了,如果有误请指正谢谢)

栅栏的操作适用于对性能和安全有极高要求的情况,否则一般情况下使用栅栏会挺麻烦的,因为区间中有多少操作这个是不确定的,所以需要我们在客户端手动设置栅栏,然后再去调用服务端的查询接口。

如果我们对性能的要求不是极高,我们可以退而求其次,只用一个线程来进行数据的增删查改,因为对数据库的操作是一个 IO 操作,如果数据量大则会明显的阻塞主线程,因此我们需要开启一条子线程来执行这些操作,为了避免数据出现错乱,我们则需要一个 FIFO 的队列来缓存这些操作,然后每次都从队列中出队一个操作,将其放在子线程中进行操作。

这样我们我们可以确保操作的实现是顺序的,而且不会阻塞主线程。

下面说一下具体的实现方式,
在 iOS 上,我们使用 GCD,关键是创建一个串行队列,代码如下

@interface RNDatabaseClient()
@property(nonatomic, strong)FMDatabase *db;
@property(nonatomic, strong)dispatch_queue_t queue; //必须是串行队列
@end
@implementation RNDatabaseClient

- (void)deleteWithSql:(NSString *)sql Callback:(id<Callback>)callback {
    dispatch_async(self.queue, ^{
        BOOL result = [self deleteWithSql:sql];
        dispatch_sync(dispatch_get_main_queue(), ^{
            if (callback != nil && [callback respondsToSelector:@selector(onResult:)]) {
                [callback onResult:result];
            }
        });
    });
}

在 Android 上,我们使用 RxJava,需要注意的是我们使用的调度器应该是Schedulers.single()
或者是核心线程数为1,最大线程数为1的线程池。

public void query(String table, String whereClause, String[] whereArgs, DataBackQueryCallback callback) {
     if (callback == null) return;
     Disposable disposable = Observable.create(emitter -> {
         List<Map<String, String>> list = query(table, whereClause, whereArgs);
         emitter.onNext(list);
     }).subscribeOn(Schedulers.single())
             .observeOn(AndroidSchedulers.mainThread())
             .subscribe((list) -> callback.onResult((List<Map<String, String>>) list));
}

总结

  1. 在对IO进行操作的时候我们需要在子线程中执行任务。
  2. 我们并发操作数据的时候需要注意并发编程带来的问题,使用栅栏隔离增删改操作,或者用一个串行队列管理所有操作。

相关文章

  • 对IO操作的正确方式

    对本地文件进行操作的时候我们需要注意多线程安全,最安全和最高效的方法是在同一时刻支持多读单写,也就是同时可以并发读...

  • java32(IO输入与输出--01概述)

    IO流: IO流用来处理设备之间的数据传输。 java对数据的操作是通过流的方式。 java用于操作流的对...

  • 异步与回调的理解

    异步 维基百科解释 异步IO是计算机操作系统对输入输出的一种处理方式:发起IO请求的线程不等IO操作完成,就继续执...

  • Kotlin之IO操作实践

    简介 在kotlin中对IO的操作有别于java,完全用java的IO方式写kotlin代码是不可取得,下...

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

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

  • day18-06-IO流(概述)/07day18-07-IO流(

    IO流用来处理设备之间的数据传输Java对数据的操作是通过流的方式 、Java用于操作流的对象都在IO包中流按操作...

  • 最全IO流解析——IO流的骚操作

    Java中是通过流的方式对数据进行操作,用于操作流的类都在IO包中,IO流用来处理设备之间的数据传输。IO流按照流...

  • linux GPIO

    GPIO(文件IO方式) gpio_operation 通过/sys/文件接口操作IO端口 GPIO到文件系统的映...

  • Java IO 操作

    IO操作 Java对数据的操作都是通过流的方式,IO流用来处理设备之间的数据传输,文件上传和文件下载,Java用于...

  • 【Java】【IO流】概述

    IO流(概述) 概述 IO流用来处理设备之间的数据传输 Java对数据的操作是通过流的方式 Java用于操作流的类...

网友评论

      本文标题:对IO操作的正确方式

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