您的位置:首页 > 移动开发 > IOS开发

iOS 同步线程的方式

2017-03-24 09:52 127 查看
同步线程说的是对多个并发线程同步

NSLock 方式

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

    [lock lock];

    [obj yourMethod];

    [lock unlock];

@synchronized(){

      [obj yourMethod];

 }

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

以下是使用的实例:

- (void)viewDidLoad {

    [super
viewDidLoad];

    //默认有10张票

    self.leftTicketsCount=10;

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

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

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

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

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

    self.thread3=[[NSThread
alloc]initWithTarget:self
selector:@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) {

                //暂停一段时间

                [NSThread
sleepForTimeInterval:0.002];

                //2.票数-1

                self.leftTicketsCount= count-1;

                //获取当前线程

                NSThread *current=[NSThread
currentThread];

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

            }else

            {

                //退出线程

                [NSThread
exit];

            }

        }

    }

}

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

{

    //开启线程

    [self.thread1
start];

    [self.thread2
start];

    [self.thread3
start];

}

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方式。NSPort有3个子类,NSSocketPort、NSMessagePort、NSMachPort,但在iOS下只有NSMachPort可用。

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

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

- (void)viewDidLoad {

    [super
viewDidLoad];

    

    //1. 创建主线程的port

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

    NSPort *myPort = [NSMachPort
port];

    

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

    myPort.delegate =
self;

    

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

    [[NSRunLoop
currentRunLoop]
addPort:myPort
forMode:NSDefaultRunLoopMode];

    

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

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

    _work = [[MyWorkerClass
alloc]
init];

    [NSThread
detachNewThreadSelector:@selector(launchThreadWithPort:)

                             toTarget:_work

                           withObject:myPort];

   

       

}

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

    

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

    

    //1. 消息id

    NSUInteger msgId = [[message
valueForKeyPath:@"msgid"]
integerValue];

    

    //2. 当前主线程的port

    NSPort *localPort = [message
valueForKeyPath:@"localPort"];

    

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

    NSPort *remotePort = [message
valueForKeyPath:@"remotePort"];

    

    if (msgId ==
kMsg1)

    {

        //向子线的port发送消息

    BOOL success=    [remotePort
sendBeforeDate:[NSDate
date]

                             msgid:kMsg2

                        components:nil

                              from:localPort

                          reserved:0];

        

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

    

        

        

    } else
if (msgId == kMsg2){

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

    }

}

@implementation MyWorkerClass

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

    

    @autoreleasepool {

        

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

        remotePort = port;

        

        //2. 设置子线程名字

        _thread = [NSThread
currentThread];

        [[NSThread
currentThread]
setName:@"MyWorkerClassThread"];

        

        //3. 开启runloop

        [[NSRunLoop
currentRunLoop] run];

        

        //4. 创建自己port

        myPort = [NSPort
port];

        

        //5.

        myPort.delegate =
self;

        

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

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

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

        [[NSRunLoop
currentRunLoop]
addPort:myPort
forMode:NSDefaultRunLoopMode];

              

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

        [self
sendPortMessage];

        

        

    }

}

/**

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

 */

- (void)sendPortMessage {

    

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

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

    [remotePort
sendBeforeDate:[NSDate
date]

                         msgid:kMsg1

                    components:array

                          from:myPort

                      reserved:0];

    

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

        [remotePort
sendBeforeDate:[NSDate
date]

                             msgid:kMsg2

                        components:nil

                              from:myPort

                          reserved:0];

}

iOS曾经提供了若干进程间通信机制CFMessagePort,但是这个在ios7之后就被废弃了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: