线程间通信
1> 线程间通信分为两种
- 主线程进入子线程(前面的方法都可以)
- 子线程回到主线程
2> 返回主线程
3> 代码
这个案例的思路是:当我触摸屏幕时,会在子线程加载图片,然后在主线程刷新UI界面
视图布局我就不写了,大家自己来吧,线程间通信代码如下:
#pragma mark - 添加响应方法触发创建子线程并加载数据 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 创建子线程 [self performSelectorInBackground:@selector(loadImage) withObject:nil]; } - (void)loadImage { NSLog(@"当前线程:%@", [NSThread currentThread]); NSLog(@"主线程:%@", [NSThread mainThread]); NSString *urlStr = @"https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1463455875&di=ef2da3f0fe711b471966aa1511483d0b&src=http://img4.duitang.com/uploads/item/201308/20/20130820094450_rsmYi.jpeg"; NSURL *url = [NSURL URLWithString:urlStr]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; self.imageData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; // 返回主线程,刷新UI [self performSelectorOnMainThread:@selector(reloadImageView) withObject:nil waitUntilDone:YES]; } - (void)reloadImageView { // 进入主线程一般进行来安全判断 if ([NSThread isMainThread]) { // 刷新UI UIImage *showIamge = [UIImage imageWithData:self.imageData]; self.showImageView.image = showIamge; } }
点击触发事件,开辟子线程成功,在主线程中刷新UI,图片显示成功
线程互斥
1> 应用场景
多线程并行编程中,线程间同步与互斥是一个很有技巧的也很容易出错的地方。
多个线程操作同一个资源(即某个对象),需要保证线程在对资源的状态(即对象的成员变量)进行一些非原子性操作后,状态仍然正确。
典型的例子是“售票厅售票应用”。售票厅剩余20张票,10个窗口去卖这些票。这10个窗口,就是10条线程,售票厅就是他们共同操作的资源,其中剩余的20张票就是这个资源的一个状态。线程买票的过程就是去递减这个剩余数量的过程。
我们看看会发生什么问题
- (void)viewDidLoad { [super viewDidLoad]; // 模拟买票系统 // 一共20张票,10个窗口卖 __block NSInteger count = 20; dispatch_queue_t ticketQueue = dispatch_queue_create("sell ticket", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 10; i ++) { dispatch_async(ticketQueue, ^{ //这里相当于每个窗口卖2张票 for (int i = 0; i < 2; i ++) { NSLog(@"买到了第%ld张票",count); count--; } }); } }
运行的效果如下图,不同的售票窗口贩卖了同一张
2> 线程互斥解决方案
- 方法一 @synchronized 自动对参数对象加锁,保证临界区内的代码线程安全(最简单的方法)
官方文档解释:
The @synchronized directive is a convenient way to create mutex locks on the fly in Objective-C code.
个人理解:
@synchronized, 代表这个方法加锁, 相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程例如B正在用这个方法,有的话要等正在使用synchronized方法 的线程B运行完这个方法后再运行此线程A,没有的话,直接运行。它包括两种用法:synchronized 方法和 synchronized 块。
@synchronized
方法控制对类(一般在IOS中用在单例中)的访问:每个类实例对应一把锁,每个 synchronized
方法都必须获得调用该方法锁方能执行,否则所属就会发生线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该
锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类,至多只有一个处于可执行状态,从而有效避免了类成员变量的访问冲突(只要所有可能访问类的
方法均被声明为 synchronized)
- (void)viewDidLoad { [super viewDidLoad]; // 模拟买票系统 // 一共20张票,10个窗口卖 __block NSInteger count = 20; __weak typeof(self) weakSelf = self; dispatch_queue_t ticketQueue = dispatch_queue_create("sell ticket", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 10; i ++) { dispatch_async(ticketQueue, ^{ // 给买票操作加锁,保证代码块只有一个线程访问 @synchronized(weakSelf) { //这里相当于每个窗口卖2张票 for (int i = 0; i < 2; i ++) { NSLog(@"买到了第%ld张票",count); count--; } } }); } }
运行结果:
- 方法二 NSLock
官方文档解释:
An NSLock object is used to coordinate the operation of multiple threads of execution within the same application. An NSLock object can be used to mediate access to an application’s global data or to protect a critical section of code, allowing it to run atomically.
个人理解:
在一个应用里面协调线程间的执行。
- (void)viewDidLoad { [super viewDidLoad]; // 模拟买票系统 // 一共20张票,10个窗口卖 __block NSInteger count = 20; // 创建线程锁 NSLock *lock = [[NSLock alloc]init]; dispatch_queue_t ticketQueue = dispatch_queue_create("sell ticket", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 10; i ++) { dispatch_async(ticketQueue, ^{ // 给买票操作加锁,保证代码块只有一个线程访问 // 加锁,不会出现多个窗口同时卖一张票的情况 [lock lock]; //这里相当于每个窗口卖2张票 for (int i = 0; i < 2; i ++) { NSLog(@"买到了第%ld张票",count); count--; } // 解锁 [lock unlock]; }); } }
以上两种方法小编进行了使用,以下两种方法大家有兴趣可以试试,小编就不进行使用了
- 方法三 NSConditionLock 条件锁 可以设置条件
官方文档解释:
The NSConditionLock class defines objects whose locks can be associated with specific, user-defined conditions. Using an NSConditionLock object, you can ensure that a thread can acquire a lock only if a certain condition is met. Once it has acquired the lock and executed the critical section of code, the thread can relinquish the lock and set the associated condition to something new. The conditions themselves are arbitrary: you define them as needed for your application.
个人理解:
根据条件加锁与解锁。
- 方法四 NSRecursiveLock 递归锁 多次调用不会阻塞已获取该锁的线程
官方文档解释:
NSRecursiveLock defines a lock that may be acquired multiple times by the same thread without causing a deadlock, a situation where a thread is permanently blocked waiting for itself to relinquish a lock. While the locking thread has one or more locks, all other threads are prevented from accessing the code protected by the lock.
个人理解:
同一个线程可以多次请求加锁,但不会引起死锁。