多线程1

###NSThread常用方法

//创建一个子线程。并自动执行
    [NSObject performSelectorInBackground]

//在主线程执行某些操作
[NSObject performSelectorOnMainThread]

//返回当前线程
[NSThread currentThread]

//创建一个新线程并自动执行
[NSThread detachNewThreadSelector]

//NSThread创建但不会自动执行的
[[NSThread alloc] initWithTarget]

//当前线程休眠几秒
    [NSThread sleepForTimeInterval:1.0f];  

//休眠到指定时间
    [NSThread sleepUntilDate:date];

####NSThread线程间通信

    [self performSelector:@selector(function) onThread:[NSThread mainThread]
     withObject:nil waitUntilDone:YES];

waitUntilDone 是否马上执行

YES:表示马上执行

NO:表示加入onThread所在线程中。等待runloop调度执行

注意:如果onThread还是当前线程,就相当于直接调用一个方法,

####NSThread线程管理生命周期管理

//启动线程
    [aThread start];

//取消当前线程的执行
    [aThread cancel];

//退出当前线程
    [NSThread exit]

###NSOperation 1.是对GCD的封装,可以理解为gcd的oc实现;

2.NSOperation自身是一个抽象类,不可直接实例化,实例化使用NSInvocationOperation和NSBlockOperation,

3.使用任务队列NSOperationQueue管理任务,

4.NSOperation比GCD的相比好处是提供了任务的暂停取消,恢复依赖等方法

###NSInvocationOperation:可以直接使用的operation

//通过selector构建
     NSInvocationOperation * operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test1) object:nil];


//直接启动,在当前线程执行
    [operation1 start];

//创建NSOperationQueue
     NSOperationQueue *queue = [[NSOperationQueue alloc] init];

//加入NSOperationQueue,会自动异步执行
   [queue addOperation:operation2];

NSInvocationOperation对象调用start是在当前线程同步执行, 加入NSOperationQueue中,会自动异步执行

###NSBlockOperation:可以直接使用的operation, 可以通过addExecutionBlock添加一个或则多个block,只有所有block执行完毕,operation才算执行完毕,用completionBlock来添加所有任务完成后下一步操作,多用于任务间的依赖

//创建blockoperation
    NSBlockOperation *operation3 = [[NSBlockOperation alloc] init];

//设置一个operation完成后的依赖,
    [operation3 setCompletionBlock:^{
        [self test3];

    }];

 //添加block
    [operation3 addExecutionBlock:^{
        [self test1];
    }];
  //添加block
    [operation3 addExecutionBlock:^{
        [self test2];
    }];

 //blockOperation的多任务是并发执行的,有的在当前线程,有的在其他线程
   // [operation3 start];


 //加入queue的operation是异步执行的
    [queue addOperation:operation4];

1.NSBlockOperation的多任务是并发执行的,有的在当前线程,有的在其他线程

2.BLockOPeration的setCompletionBlock设置完成operation后依赖

###通用operation操作:设置优先级,设置依赖,手动执行operation, 取消operation //设置operation优先级

[operation3 setQueuePriority:NSOperationQueuePriorityHigh];

//设置operation的依赖

[operation1 addDependency:operation3];

手动执行operation 就是调用start方法

在默认的start方法会自动检查operation是非准备好,是非被取消,并生成一些相关的KVO通知,如isExcuting isFinished 来处理operation之间的依赖关系。

isReady为YES就是准备好,准备好的前提是它所依赖的operation已经执行完成),(isCanner如果为yes就是已经取消,那就不用做了)

取消operation

operation加入operation queue中后,所有权就属于queue , 可以调用queue的cancelAllOperationsl取消所有任务 取消单个operation方法就是调用operation的取消方法来移除,但不一定奏效

[operation3 cancel]; isCanCel就为YES,此时isFinished也会变为YES。则会执行completeBLock

###自定义OPeration 1.继承与OPeratoion

2.实现main方法,在main中实现下载等自定义操作,然后就可以当成普通的operation一样来处理了

##NSOperationQueue任务队列:用于管理任务(Operation)

加入队列的任务会自动执行,而且默认是异步的并发执行,

###队列分类和任务添加 主队列,加入的任务是并行的,第一个在主线程,其他的在其他线程

    [[NSOperationQueue mainQueue] addOperation:operation3];

其他队列,加入的任务是并行的,而且是异步的

    NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
    [queue1 addOperation:operation3];

1.添加到quete中operation对象的随时有可能执行,因此加入quete后不可修改operation的状态。

2.队列中operation的执行顺序决定因素有2个:

1.operation是否准备好,由operation间的依赖关系决定

2.operation的优先级,默认都是普通级别。

先看依赖关系,然后从准备好的优先级最高的那个执行

##队列暂停和恢复

//YES表示暂停,NO表示恢复
    [queue1 setSuspended:YES];

##设置队列queue的最大并发数maxConcurrentOperationCount:就是operation的个数,

不是线程个数,不能保存串行,因为存在blockoperation

一个block是一个线程,一个operation会存在多个block,就是多个线程

##GCD:全称Grand Central Dispatch 是NSoperation的底层实现,是C语言级别的多线程

gcd中使用block代码块来表示一个任务,使用队列来管理任务

###GCD队列分类 1.主队列:dispatch_get_main_queue()

2.全局队列:dispatch_get_global_queue(0, 0);

3.用户创建队类:dispatch_queue_create("sdfsaklf", DISPATCH_QUEUE_CONCURRENT);

DISPATCH_QUEUE_CONCURRENT:并行队列 DISPATCH_QUEUE_SERIAL: 串行队列

##GCD同步执行和异步执行 GCD同步执行:dispatch_sync GCD异步执行:dispatch_async

##GCD队列组:dispatch_group_t 可以将多个队列添加一个队列组中, 好处是: 组中所有任务执行完毕,会通过队列组的dispatch_group_notify来通知我们。 主要用于GCD的多线程数据同步

##GCD的并发控制,使用信号量semaphore来实现。 用于控制并发执行的线程的个数。免得线程过多把cup给累死 1.创建信号量 dispatch_semaphore_create

 dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);

2.等待信号。dispatch_semaphore_wait

用于执行任务前的判断,如果信号量大于0就可以执行,并将信号量的值减一,小于0就会一直等待

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

3.发送一个信号量,dispatch_semaphore_signal

用于任务完成后增加信号量的值,信号量的值加一

 dispatch_semaphore_signal(semaphore);

###GCD与NSOPeration CGD

GCD创建的线程,系统自动管理其生命周期,程序无法控制,

但是我们可以通过添加条件来停止线程中的任务。来让线程没有任务做,而减少cpu的消耗

NSOperation

可以添加任务依赖,来保证任务执行顺序的正确性

已开启的任务可以暂停,取消,恢复,以及设置优先级

##线程安全 线程不安全概念

多线程对公用资源和数据的竞争导致的资源和数据不同步叫做线程不安全

实际就是多个线程同时操作某个对象从而导致其状态的不一致。

线程同步(线程安全): 就是不允许 在同一时刻 在不同线程中 操作同一对象,

说白了就是一个对象只有一个线程访问结束才可以接受下一个线程的访问。

线程同步实现方法

1.互斥锁synchronized:

原理

@synchronized后面是一个oc对象,实际上是把这个对象当作锁来使用,作为该锁的唯一表识。只有当表示相同时,才会满足互斥,底层原理是,当调用synchronized的每个对象是,runtime会为其分配一个递归锁并存储在哈希表中

@synchronized(self) {
    //需要执行的代码块
}

2.线程锁NSLock, 简单的互斥锁,实现了NSLocking protocol

原理 锁是最常用的同步工具。一段代码段在同一个时间只能允许被一个线程访问,

如一个线程A进入加锁代码之后由于已经加锁,另一个线程B就无法访问,只有等待前一个线程A执行完加锁代码后解锁,B线程才能访问加锁代码。

注意lock与unlock对应,不能嵌套使用

_lock = [[NSLock alloc] init];

    [_lock Lock];
    //需要一次执行完的代码段{}
    [_lock unlock];

###NSCondition最基本的条件锁,继承与NSLock用于手动控制线程wait和signal

[condition lock];

一般用于多线程同时访问、修改同一个数据源,保证在同一时间内数据源只被访问、修改一次,其他线程的命令需要在lock 外等待,只到unlock ,才可访问

[condition unlock];与lock 同时使用

[condition wait];

让当前线程处于等待状态,会自动调用unlock,

[condition signal];

CPU发信号告诉正在等待的线程不用等待,可以继续执行,所有wait的线程都有机会继续执行,获得机会的condition后会自动先lock一次, 必须保证wait的unlock成对,然后执行。

##生产者消费者实例

 NSCondition *condition = [[NSCondition alloc] init];

    NSMutableArray *products = [NSMutableArray arrayWithCapacity:0];
 //消费者
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (1) {
            //加锁
            [condition lock];
            if([products count]==0)
            {
                NSLog(@"wait for Product");
                //wait 让当前线程处于等待状态
                [condition wait];
            }
            //消费一个产品
            [products removeObjectAtIndex:0];
            NSLog(@"custome a product");
            //解锁
            [condition unlock];


        }
    });

    //生产者
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (1) {
            //加锁
            [condition lock];
                //制作一个产品
            NSLog(@"make a product");

            [products addObject:[[NSObject alloc] init]];
            //所有处于wait的线程,均有机会开始执行
            [condition signal];
            //解锁
            [condition unlock];
            sleep(1);
        }
    });

1.所有的锁都继承NSLock,实现了NSLocking

2.当锁对象调用lock后,其他的对此对象的lock调用都会被阻塞。即一个对象lock 只有等到unlock后才可以再次lock

3.wait函数内部会自动调用了unlock函数,让线程中NSCondition的加锁对象处于无锁的状态,当wait所在的线程被signal触发而再次运行,会自动lock一次。

###NSLock的分类 NSConditionLock条件锁 NSRecursiveLock递归锁

队列是先进先出的,FIFO

###死锁 通常指两个线程a和b都卡住了,

即a在等待b完成才可以继续执行,b在等待a完成才可以继续执行, 所以a不能完成是因为b未完成,b未完成是因为a未完成,这样大家都完成不了,所以导致了死锁。

出现死锁的条件:只有串行队列的上的线程的同步执行才会死锁,

因为同步会阻塞当前线程,串行任务要等待当前线程的任务完成才可以执行,

这样一个阻塞,一个等待就成了相互等待,所以死锁

即 同步 且在 当前线程的串行任务 会导致当前线程死锁。

https://blog.csdn.net/huangyongf/article/details/52199175

###线程安全的实现方式有 1.synchronized 2.NSLock 3.GCD的信号量

##同步串行执行任务:

就是把多个线程都要执行的代码块添加到一个串行队列中

###使用atomic一定是线程安全的吗

No.atimaic是对属性的修饰词,是说属性的读和写是原子性的, 也就是说getter 和setter 方法是安全,但不代表线程安全,只是增加了线程安全的几率,

举例说明:a线程写操作时,其他线程会因为原子属性而等待,a写完后。b 写,此时若a要读取就必须等待b完成,但b完成后a读取的是b修改后的值,这样就已经破坏了线程安全,如果此时有一个c在a读取前release 更会导致程序崩溃,要想线程安全必须给线程添加lock。

一句话,atomic只是保证了getter 和setter方法的线程安全,并不能保证整个属性对象的线程安全。

####NSThread线程状态判断

//判断当前线程是否是主线程
    [NSThread isMainThread]

//判断程序是否是多线程

    [NSThread isMutalThread]

//线程是否在执行

    [aThread isExecuting];

//线程是否已经结束 

    [aThread isFinished];
//线程是否被取消

    [aThread isCancelled];
JSRUN前端笔记, 是针对前端工程师开放的一个笔记分享平台,是前端工程师记录重点、分享经验的一个笔记本。JSRUN前端采用的 MarkDown 语法 (极客专用语法), 这里属于IT工程师。