iOS 同步线程的方式

来源:互联网 发布:微信windows phone旧版 编辑:程序博客网 时间:2024/06/05 14:24

同步线程说的是对多个并发线程同步

NSLock 方式

 NSLock *lock = [[NSLock alloc]init];

    [lock lock];

    [obj yourMethod];

    [lock unlock];


@synchronized(){

      [obj yourMethod];

 }

以上的代码[obj yourMethod];是关键的代码每次只能被一个线程调用。

以下是使用的实例:

- (void)viewDidLoad {

    [superviewDidLoad];

    //默认有10张票

    self.leftTicketsCount=10;

    //开启多个线程,模拟售票员售票

    self.thread1=[[NSThreadalloc]initWithTarget:selfselector:@selector(sellTickets)object:nil];

    self.thread1.name=@"售票员A";

    self.thread2=[[NSThreadalloc]initWithTarget:selfselector:@selector(sellTickets)object:nil];

    self.thread2.name=@"售票员B";

    self.thread3=[[NSThreadalloc]initWithTarget:selfselector:@selector(sellTickets)object:nil];

    self.thread3.name=@"售票员C";

}

/*

 这种方法是不安全的

-(void)sellTickets

{

    while (1) {

        //1.先检查票数

        int count=self.leftTicketsCount;

        if (count>0) {

            //暂停一段时间

            [NSThread sleepForTimeInterval:0.002];

            //2.票数-1

            self.leftTicketsCount= count-1;

            

            //获取当前线程

            NSThread *current=[NSThread currentThread];

            NSLog(@"%@--卖了一张票,还剩余%d张票",current,self.leftTicketsCount);

        }else

        {

            //退出线程

            [NSThread exit];

        }

    }

}

*/


-(void)sellTickets

{

    while (1) {

        @synchronized(self){//只能加一把锁

            //1.先检查票数

            int count=self.leftTicketsCount;

            if (count>0) {

                //暂停一段时间

                [NSThreadsleepForTimeInterval:0.002];

                //2.票数-1

                self.leftTicketsCount= count-1;

                //获取当前线程

                NSThread *current=[NSThreadcurrentThread];

                NSLog(@"%@--卖了一张票,还剩余%d张票",current,self.leftTicketsCount);

            }else

            {

                //退出线程

                [NSThreadexit];

            }

        }

    }

}


-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

{

    //开启线程

    [self.thread1start];

    [self.thread2start];

    [self.thread3start];

}


GCD 两种方式 :dispatch_group  dispatch_semaphore

- (void)queueGroup {

    dispatch_queue_t conCurrentGlobalQueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

    dispatch_queue_t mainQueue =dispatch_get_main_queue();

    dispatch_group_t groupQueue =dispatch_group_create();

    NSLog(@"current task");

    dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{

        NSLog(@"并行任务1");

    });

    dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{

        NSLog(@"并行任务2");

    });

    dispatch_group_notify(groupQueue, mainQueue, ^{

        NSLog(@"groupQueue中的任务都执行完成,回到主线程更新UI");

    });

    NSLog(@"next task");

}


 

dispatch_semaphore是GCD用来同步的一种方式,与他相关的共有三个函数,分别是

dispatch_semaphore_create,dispatch_semaphore_signal,dispatch_semaphore_wait

以下是参考 http://www.cnblogs.com/snailHL/p/3906112.html

(1)dispatch_semaphore_create的声明为:

  dispatch_semaphore_t  dispatch_semaphore_create(long value);

 传入的参数为long,输出一个dispatch_semaphore_t类型且值为value的信号量。

 值得注意的是,这里的传入的参数value必须大于或等于0,否则dispatch_semaphore_create会返回NULL。

(2)dispatch_semaphore_signal的声明为:

  long dispatch_semaphore_signal(dispatch_semaphore_t dsema)

  这个函数会使传入的信号量dsema的值加1;(至于返回值,待会儿再讲)

 

 (3) dispatch_semaphore_wait的声明为:

  long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);

  这个函数会使传入的信号量dsema的值减1;

  这个函数的作用是这样的,如果dsema信号量的值大于0,该函数所处线程就继续执行下面的语句,并且将信号量的值减1;

  如果desema的值为0,那么这个函数就阻塞当前线程等待timeout(注意timeout的类型为dispatch_time_t,

  不能直接传入整形或float型数),如果等待的期间desema的值被dispatch_semaphore_signal函数加1了,

  且该函数(即dispatch_semaphore_wait)所处线程获得了信号量,那么就继续向下执行并将信号量减1。

  如果等待期间没有获取到信号量或者信号量的值一直为0,那么等到timeout时,其所处线程自动执行其后语句。

  

(4)dispatch_semaphore_signal的返回值为long类型,当返回值为0时表示当前并没有线程等待其处理的信号量,其处理

  的信号量的值加1即可。当返回值不为0时,表示其当前有(一个或多个)线程等待其处理的信号量,并且该函数唤醒了一

  个等待的线程(当线程有优先级时,唤醒优先级最高的线程;否则随机唤醒)。

  dispatch_semaphore_wait的返回值也为long型。当其返回0时表示在timeout之前,该函数所处的线程被成功唤醒。

  当其返回不为0时,表示timeout发生。

 

(5)在设置timeout时,比较有用的两个宏:DISPATCH_TIME_NOW 和 DISPATCH_TIME_FOREVER。

  DISPATCH_TIME_NOW  表示当前;

  DISPATCH_TIME_FOREVER  表示遥远的未来;

  一般可以直接设置timeout为这两个宏其中的一个,或者自己创建一个dispatch_time_t类型的变量。

  创建dispatch_time_t类型的变量有两种方法,dispatch_time和dispatch_walltime。

  利用创建dispatch_time创建dispatch_time_t类型变量的时候一般也会用到这两个变量。

  dispatch_time的声明如下:

  dispatch_time_t dispatch_time(dispatch_time_t when, int64_t delta);

  其参数when需传入一个dispatch_time_t类型的变量,和一个delta值。表示when加delta时间就是timeout的时间。

  例如:dispatch_time_t  t = dispatch_time(DISPATCH_TIME_NOW, 1*1000*1000*1000);

     表示当前时间向后延时一秒为timeout的时间。

 线程之间通信也是同步线程的一种方式

线程间通信的体现1个线程传递数据给另1个线程在1个线程中执行完特定任务后,转到另1个线程继续执行任务

 线程间通信常用方法代码如下:

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;


第二种通信为NSMachPort方式。NSPort3个子类,NSSocketPortNSMessagePortNSMachPort,但在iOS下只有NSMachPort可用。

 使用的方式为接收线程中注册NSMachPort,在另外的线程中使用此port发送消息,则被注册线程会收到相应消息,然后最终在主线程里调用某个回调函数。 使用NSMachPort的结果为调用了其它线程的1个函数,而这正是performSelector所做的事情,所以,NSMachPort是个鸡肋。线程间通信应该都通过performSelector来搞定。

以下是NSPort 使用的实例代码:

- (void)viewDidLoad {

    [superviewDidLoad];

    

    //1. 创建主线程的port

    // 子线程通过此端口发送消息给主线程

    NSPort *myPort = [NSMachPortport];

    

    //2. 设置port的代理回调对象

    myPort.delegate =self;

    

    //3. port加入runloop,接收port消息

    [[NSRunLoopcurrentRunLoop]addPort:myPortforMode:NSDefaultRunLoopMode];

    

    NSLog(@"---myport %@", myPort);

    //4. 启动次线程,并传入主线程的port

    _work = [[MyWorkerClassalloc] init];

    [NSThreaddetachNewThreadSelector:@selector(launchThreadWithPort:)

                             toTarget:_work

                           withObject:myPort];

   

       

}

- (void)handlePortMessage:(NSMessagePort*)message{

    

    NSLog(@"接到子线程传递的消息!%@",message);

    

    //1. 消息id

    NSUInteger msgId = [[messagevalueForKeyPath:@"msgid"]integerValue];

    

    //2. 当前主线程的port

    NSPort *localPort = [messagevalueForKeyPath:@"localPort"];

    

    //3. 接收到消息的port(来自其他线程)

    NSPort *remotePort = [messagevalueForKeyPath:@"remotePort"];

    

    if (msgId ==kMsg1)

    {

        //向子线的port发送消息

    BOOL success=    [remotePortsendBeforeDate:[NSDatedate]

                             msgid:kMsg2

                        components:nil

                              from:localPort

                          reserved:0];

        

        NSLog(@"%@",_work.thread);

    

        

        

    } elseif (msgId == kMsg2){

        NSLog(@"操作2....\n");

    }

}



@implementation MyWorkerClass

- (void)launchThreadWithPort:(NSPort *)port {

    

    @autoreleasepool {

        

        //1. 保存主线程传入的port

        remotePort = port;

        

        //2. 设置子线程名字

        _thread = [NSThreadcurrentThread];

        [[NSThreadcurrentThread]setName:@"MyWorkerClassThread"];

        

        //3. 开启runloop

        [[NSRunLoopcurrentRunLoop] run];

        

        //4. 创建自己port

        myPort = [NSPortport];

        

        //5.

        myPort.delegate =self;

        

        //6. 将自己的port添加到runloop

        //作用1、防止runloop执行完毕之后推出

        //作用2、接收主线程发送过来的port消息

        [[NSRunLoopcurrentRunLoop]addPort:myPortforMode:NSDefaultRunLoopMode];


              

        //7. 完成向主线程port发送消息

        [selfsendPortMessage];

        

        

    }

}


/**

 *   完成向主线程发送port消息

 */

- (void)sendPortMessage {

    

    NSMutableArray *array  =[[NSMutableArrayalloc]initWithArray:@[@"1",@"2"]];

    //发送消息到主线程,操作1

    [remotePortsendBeforeDate:[NSDatedate]

                         msgid:kMsg1

                    components:array

                          from:myPort

                      reserved:0];

    

    //发送消息到主线程,操作2

        [remotePortsendBeforeDate:[NSDatedate]

                             msgid:kMsg2

                        components:nil

                              from:myPort

                          reserved:0];

}




iOS曾经提供了若干进程间通信机制CFMessagePort,但是这个在ios7之后就被废弃了。















0 0