iOS并发编程(一)——thread

      当目前为止,你的iOS代码是不是都只有一个mainThread,你的程序一直都是串行的,总是干完任务A再去干任务B,哪怕AB之间完全没有依赖关系。mainThread是用来处理与UI相关的事件的,如果你在mainThread中执行一些需要耗费大量时间的任务(比如从网络下载数据),那么这个程序的用户体验将是极差的,因为用户往往需要等待很久。
重要:任何与UI相关的操作都应该放在mainThread中来处理。
      同时,要知道现在是个移动设备都在标称自己是多核的,你好意思让你的程序只占用其中一个,而让另外的都自顾自儿的空转么?!我想我没有必要去阐述多线程编程有什么哪些优点,会带来什么好处,因为大家都懂的。
      当然,要实现程序的并发,也并不要求运行它的设备一定是多核的,我们可以通过对CPU的调度(时间片轮转)来实现”表明上“的同时执行。在开始这篇文章之前,有必要先解释一下几个关键性的术语:
      thread(线程)——可简单理解为可独立执行的代码段。
      process(进程)——指一个正在运行的可执行程序,一个进程可以包含多个线程。
      task(任务)——对程序执行的工作的抽象。
      concurrency(并发)——指程序可以同时或者几乎同时执行多个任务。

      前面提到,即使是在单核CPU的情况下,也可以实现程序的并发。比如,在1s之内有10个相同优先级的task需要执行,系统可以将1s的CPU时间均分为10个100ms,并把这10个100ms的CPU时间分配给10个task。那么,表面上看来,在1s的CPU时间内这10个task同时执行了。 但是,现在的系统已经实现了真正的多核,没有必要这么吝啬的在一个CPU上去瓜分时间片,况且CPU的调度也是耗费资源和时间的。

1.1 利用线程实现并发

      在iOS下实现程序的并发,将是前所未有的简单。最直接的:
[self performSelectorInBackground:@selector(try:) withObject:para1];
        简单的调用一下performSelectorInBackground:withObject:函数,把你想要并发执行的函数(任务)传给第一个参数,如示例中的try,第二个参数para1是try函数的参数。这样系统就自动为你开辟了一个后台线程,你的任务已经是在后台而不是mainThread了。
     当然你也可以自己手动创建一个新的Thread,如下:
[NSThread detachNewThreadSelector:@selector(try:)  toTarget:self withObject:para1]
     这是类方法。也可以创建一个Thread实例:
NSThread* myThread = [[NSThread alloc] initWithTarget:self selector:@selector(myThreadMainMethod:)  object:nil];
//start thread manually
[myThread start];

   为了防止内存泄露,如果你使用的是内存管理模型(即含有retainrelease操作)你应该在try中添加autoreleasepool,main函数中做的那样:

-(void)try:(id)para1{
    @autoreleasepool{
        
        /*do something here*/
    }
}

     你调用后台线程时,也应该这么做,这两种方式并没有实质性的区别。如果你使用的是垃圾回收机制(ARC),那么创建的自动释放池会被忽略,但它是无害的。

    如果你创建了多个Thread实例,你可以通过:

performSelector:onThread:withObject:waitUntilDone:

方法来调用它们执行相应的task。
     你还可以对创建的Thread实例进行相应的配置。

1.1.1 堆栈

     你在程序中创建的每一个线程,系统都会在进程空间中分配一部分内存空间作为该线程的堆栈,该堆栈管理线程的数据,比如线程中所有的局部变量都将在堆栈里。你可以调用下列函数来配置和访问线程堆栈:
- (NSUInteger)stackSize NS_AVAILABLE(10_5, 2_0);
- (void)setStackSize:(NSUInteger)s NS_AVAILABLE(10_5, 2_0);
+ (NSArray *)callStackReturnAddresses NS_AVAILABLE(10_5, 2_0);
+ (NSArray *)callStackSymbols NS_AVAILABLE(10_6, 4_0);

1.1.2 线程的键-值字典

     每个线程都维护了一个键-值字典,该字典中保存了该线程的一些状态信息,字典在线程运行的过程中随时都可以访问。你可以使用NSThread的threadDictionary方法来获取一个NSMutableDictionary对象,并在里面添加线程需要的键值对。
- (NSMutableDictionary *)threadDictionary;

1.1.3  线程的运行状态

         配置线程何时运行,何时退出,以及得知线程当前的运行状态。
      让线程先睡眠一段时间,然后自动运行:
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
      取消线程执行,开始线程,退出线程:
- (void)cancel NS_AVAILABLE(10_5, 2_0);
- (void)start NS_AVAILABLE(10_5, 2_0);
+ (void)exit;

      查询线程的状态:

- (BOOL)isExecuting NS_AVAILABLE(10_5, 2_0);
- (BOOL)isFinished NS_AVAILABLE(10_5, 2_0);
- (BOOL)isCancelled NS_AVAILABLE(10_5, 2_0);

1.1.4  优先级

     在默认的情况下,创建的新线程的优先级与当前线程是相同的。内核调度算法在决定该运行那个线程时,会把线程的优先级作为考量因素,较高优先级的线程会比较低优先级的线程具有更多的运行机会。你可以通过:

//class method
+ (double)threadPriority;
+ (BOOL)setThreadPriority:(double)p;
//instance method
- (double)threadPriority NS_AVAILABLE(10_6, 4_0);
- (void)setThreadPriority:(double)p NS_AVAILABLE(10_6, 4_0);

来设置线程的优先级。

重要:让你的线程处于默认优先级值是一个不错的选择。增加某些线程的优先级,同时有可能增加了某些较低优先级线程的饥饿程度。如果你的应用程序包含较高优先级和较低优先级线程,而且它们之间必须交互,那么较低优先级的饥饿状态有可能阻塞其他线程,并造成性能瓶颈。





你可能感兴趣的:(thread,多线程,ios,并发)