zoukankan      html  css  js  c++  java
  • ios多线程环境下数据库架构

          在IOS程序设计中,一般都需要管理本地化数据。apple为我们提供多种方式来本地化数据比如:core data,一般的平面文件,当然还有sqlite。core data在苹果的官方文档中说是一个高级功能,不建议新手程序员使用。我粗略的研究了一下core data,毕竟是苹果原生的东西。在很多地方,与苹果原生的系统结合的非常好。比如可以直接将core data作为UITableView的数据源来使用,无论是从编程效率还是程序的优雅型上提高了很多。但是,正如苹果说的那样这毕竟是一项高级功能,在使用起来还是有点费劲的,尤其是在多线程环境下。因为使用管了sqlite,所以在本地化的时候就多看了一下sqlite的东西。
          apple只提供了sqlite的库文件,如果想使用sqlite,还是得自己再去封装一层sqlite。开始,我用C++封装了简单的封装了一层数据库,方便上层调用。但是在使用的过程中越到了多线程的问题。sqlite在多线程环境下有三种运行模式:参见(http://blog.csdn.net/diyagoanyhacker/article/details/7209888)
    单线程:禁用所有的mutex锁,并发使用时会出错。当SQLite编译时加了SQLITE_THREADSAFE=0参数,或者在初始化SQLite前调用sqlite3_config(SQLITE_CONFIG_SINGLETHREAD)时启用。 
    多线程:只要一个数据库连接不被多个线程同时使用就是安全的。源码中是启用bCoreMutex,禁用bFullMutex。实际上就是禁用数据库连接和prepared statement(准备好的语句)上的锁,因此不能在多个线程中并发使用同一个数据库连接或prepared statement。当SQLite编译时加了SQLITE_THREADSAFE=2参数时默认启用。若SQLITE_THREADSAFE不为0,可以在初始化SQLite前,调用sqlite3_config(SQLITE_CONFIG_MULTITHREAD)启用;或者在创建数据库连接时,设置SQLITE_OPEN_NOMUTEX flag。 
    串行:启用所有的锁,包括bCoreMutex和bFullMutex。因为数据库连接和prepared statement都已加锁,所以多线程使用这些对象时没法并发,也就变成串行了。当SQLite编译时加了SQLITE_THREADSAFE=1参数时默认启用。若SQLITE_THREADSAFE不为0,可以在初始化SQLite前,调用sqlite3_config(SQLITE_CONFIG_SERIALIZED)启用;或者在创建数据库连接时,设置SQLITE_OPEN_FULLMUTEX flag。、
         苹果提供的库在测试后发现,他们在编译的时候使用的是第二种方式。第二种方式,解决多个线程读写同一个数据库的问题。但是同一个数据库连接不能够多个线程间共享。一个解决方案是为每一个线程都生成一个数据库连接。于是得频繁的打开和关闭数据库连接。虽然打开和关闭sqlite消耗的时间并不是很大,但是每次打开都会系统会为这个数据库连接开辟一段内存作为缓存,但是关闭库连接的时候并没有回收(回收机制不太明确)。而苹果建议手机程序占用内存不能超过20MB,这样在多次打开关闭后,因为数据库缓存的问题,可能会引起程序的内存占用量激增超过20MB。于是用没有一种折中的方案呢。我在网上找了很多ios上sqlite的开源库,最终发现了很多程序都在用的FMDB(https://github.com/ccgus/fmdb)。这个库对于多线程使用blocks的方式进行处理,可以在多个线程之间共享数据库连接。使用这个库就可以在整个程序运行期间只维护几个数据库连接。
     
         整个程序在多线程环境下的数据库架构就清晰了起来。
         数据库连接类WizDataBase,保持一个FMDB的数据库连接,并提供针对程序的数据库查询方法。这个类实现WizDataBaseDeleage协议。WizDataBaseDeleage协议定义了程序中需要进行数据交互的所有数据库操作。然后
    /*
    WizDataBaseDelegate.h
    */
    @protocol WizDataBaseDelegate
    
    // 具体的接口
    @end

    /*
    WizDataBase.h
    */
    @interface WizDataBase<WizDataBaseDelegate>
    {
      FMDB* fmdb;
    ......
    }
    @end
         数据库管理类(单例)WizDataBaseManager负责维护数据连接。实际上提供的是满足WizDataBaseDeleage协议的对象。而不是一个具体的WizDataBase对象。这样就可以通过类似与接口的方式,将数据库层的具体实现屏蔽,只关心所需要的具体借口。
      
    #import "WizDataBase.h"
    #import "WizDataBaseDelegate.h"
    
    @interface WizDbManager()
    {
        NSMutableDictionary* dbDataDictionary;
    }
    @property (atomic, retain) NSMutableDictionary* dbDataDictionary;
    - (void) clearDataBase;
    - (id<WizDataBaseDelegate>) getWizDataBase;
    @end
    
    @implementation WizDbManager
    @synthesize dbDataDictionary;
    - (void) dealloc
    {
        [dbDataDictionary release];
        [super dealloc];
    }
    ...........
    
    @end
          在具体使用的使用向WizDataBaseManager请求一个WizDataBase,然后使用。在这特意提一下一种常见的数据库应用场景,就是在使用MVC的使用模型控制器从数据库中读取数据然后去更新UI。读取数据库当然是一个很费事的操作,如果让主线程一直忙于读取数据库的话,将会降低UI的响应,让界面看起来很卡。于是就很有必要将读取数据的操作使用多线程的方式处理。apple提供很多多线程处理方式,NSOperation、GCD、Thread。在这种场景下,最适合的是GCD。写个例子:
       
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
            if (nil == documentGuid) {
                return;
            }
            id<WizDataBaseDelegate> dataBase = [[WizDataBaseManager shareDbManager] getWizTempDataBase];
            WizAbstract* abstract = [dataBase abstractOfDocument:documentGuid];
            dispatch_async(dispatch_get_main_queue(), ^{
                if (nil == abstract) {
                    return;
                }
                [self.data setObject:abstract forKey:documentGuid];
               imageView.image = abstract.image;
            });
        });
  • 相关阅读:
    Android_AyscnTask
    Android_handler
    Android_网络操作
    Android_网络基础
    Android_Fragment
    Android_activity 生命周期
    Android-Dialog
    android_menu
    Android-约束布局
    andorid_相对布局小练习
  • 原文地址:https://www.cnblogs.com/yishuiliunian/p/2597864.html
Copyright © 2011-2022 走看看