当前位置:   article > 正文

多线程技术(二)_两个线程同时修改同一条数据时怎么增加并发

两个线程同时修改同一条数据时怎么增加并发

一、如果多个子线程同时修改/更新同一个变量的值,造成数据不一致现象。

[Demo01_SellTicket]

分析问题: 多个子线程同时修改同一个值

解决方案: 适当的时候“加锁”;适当的时候“解锁”

  1. <span style="font-size:14px;"><span style="font-size:14px;">@interface ViewController ()
  2. /**剩余的票数*/
  3. @property (nonatomic, assign) int leftTicketCount;
  4. /**声明互斥锁的属性*/
  5. @property (nonatomic, strong) NSLock *lock;
  6. @end
  7. @implementation ViewController
  8. - (void)viewDidLoad {
  9. [super viewDidLoad];
  10. // 初始化lock对象
  11. self.lock = [[NSLock alloc] init];
  12. // 给定北京->深圳总票数
  13. NSNumber *totalTicketCount = @60;
  14. self.leftTicketCount = [totalTicketCount intValue];
  15. // 1.创建两个NSThread对象
  16. NSThread *firstThreadWindow = [[NSThread alloc] initWithTarget:self selector:@selector(sellTicket:) object:totalTicketCount];
  17. firstThreadWindow.name = @"窗口一";
  18. NSThread *secondThreadWindow = [[NSThread alloc] initWithTarget:self selector:@selector(sellTicket:) object:totalTicketCount];
  19. secondThreadWindow.name = @"窗口二";
  20. // 2.在子线程执行卖票逻辑
  21. // 3.启动卖票动作
  22. [firstThreadWindow start];
  23. [secondThreadWindow start];
  24. }
  25. - (void)sellTicket:(NSNumber *)totalCount {
  26. //下面的所有逻辑在子线程中执行整个卖票
  27. while (1) {
  28. //加锁
  29. [self.lock lock];
  30. if (self.leftTicketCount > 0) {
  31. //模拟延时操作(找个零钱...)
  32. [NSThread sleepForTimeInterval:0.1];
  33. //还有的卖
  34. self.leftTicketCount--;
  35. NSLog(@"窗口:%@;剩余票数:%d", [NSThread currentThread], self.leftTicketCount);
  36. //解锁
  37. [self.lock unlock];
  38. }else{
  39. //卖完了
  40. NSLog(@"票已经全部卖完了");
  41. //解锁
  42. [self.lock unlock];
  43. break;
  44. }
  45. }
  46. }</span></span>

总结:

1. 最好不要使用加锁 / 解锁,一般情况让服务器端做

2. 很少使用pthread / NSThread具体创建线程

3. [NSThread currentThread]: number=<?> name=<?>

4. 创建NSThread对象

a. 方式二: 调用形式相对简单

-》 创建子线程,并且同时执行sellTicket方法

[NSThread detachNewThreadSelector:@selector(sellTicket:) toTarget:self withObject:nil];

b. 方式一: 灵活性相对较高(适当的时候调用start)

NSThread *firstThreadWindow = [[NSThreadalloc]initWithTarget:selfselector:@selector(sellTicket:)object:totalTicketCount];

    [firstThreadWindow start];

5. threadPriority: 线程优先级; 取值范围为:[0, 1];值越大, CPU调度的几率提高

二、GCD

  1. <span style="font-size:14px;"></pre><span style="color: rgb(51, 51, 51); font-family: arial, 宋体, sans-serif; line-height: 24px; text-indent: 28px;"><span style="font-size:14px;">Grand Central Dispatch (GCD)是Apple开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并行任务。</span></span></p><p class="p1"><div style="text-indent: 28px;"><span style="font-family:arial, 宋体, sans-serif;font-size:14px;color:#333333;"><span style="line-height: 24px;"></span></span><p class="p1"><span class="s1">1. 如何使用?</span></p><p class="p1"><span class="s1">1.0 一般的GCD的执行任务的流程</span></p><p class="p1"><span class="s1">    a. 创建队列</span></p><p class="p1"><span class="s1">    b. 给定任务,放到队列中</span></p><p class="p1"><span class="s1">    c. 执行队列中的任务</span></p><p class="p1"><span class="s1">1.1 队列类型</span></p><p class="p1"><span class="s1">     串行队列(Serial Queue):      </span><span class="s2">顺序</span><span class="s1">地执行</span></p><p class="p1"><span class="s1">     并行队列(Concurrent Queue): </span><span class="s2">同时</span><span class="s1">地执行</span></p><p class="p1"><span class="s1">     系统默认创建主队列(Main Queue): </span><span class="s2">主线程顺序</span><span class="s1">地执行</span></p><p class="p1"><span class="s1">     -》由主线程执行的串行队列</span></p><p class="p1"><span class="s1">     系统默认创建全局队列(Global Queue):</span><span class="s2">同时</span><span class="s1">地执行</span></p><p class="p1"><span class="s1">     -》已经创建好的并行队列</span></p><p class="p1"><span class="s1">1.2 执行任务的方式</span></p><p class="p1"><span class="s1">      同步执行(Sync):   当前线程 + 等待任务执行完毕</span></p><p class="p1"><span class="s1">      异步执行(Async): 子线程 + 立即返回(不等待任务)</span></p><p class="p1"><span class="s1">1.3 明确知道两点:</span></p><p class="p1"><span class="s1">     a. 任务的执行顺序: 打印log,看时间</span></p><p class="p1"><span class="s1">     b. 有谁执行任务(主线程还是子线程):[NSThread currentThread]</span></p><p class="p1"><span class="s1">3.4 从多个排列组合中,寻找可以完成需求的组合</span></p><p class="p1"><span class="s1">    -> 子线程同时执行多个任务的组合</span></p><p class="p2"><span class="s3">        </span><span class="s2">a. </span><span class="s1">并行队列异步执行</span></p><p class="p2"><span class="s1">        b. 全局队列异步执行 (更常用)</span></p><p class="p1"><span class="s1">    -> 回到主线程的组合</span></p><p class="p1"><span class="s1">        </span><span class="s4">主队列异步执行</span></p></div><pre name="code" class="objc"><span style="font-size:14px;">- (void)viewDidLoad {
  2. [super viewDidLoad];
  3. // 正确认识"当前线程"
  4. dispatch_queue_t queue = dispatch_queue_create("ThirdConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
  5. dispatch_async(queue, ^{
  6. NSLog(@"并行队列异步执行开始%@", [NSThread currentThread]);
  7. //在同步执行任务
  8. //当前线程为子线程
  9. dispatch_sync(queue, ^{
  10. NSLog(@"xxxxx:%@", [NSThread currentThread]);
  11. });
  12. });
  13. }
  14. /**串行队列同步执行*/
  15. - (IBAction)serialQueueSync:(id)sender {
  16. /*明确点:任务的执行顺序; 主线程还是子线程执行
  17. 串行队列: 顺序地执行
  18. 同步执行: 当前线程 + 等待任务执行完毕
  19. */
  20. //1.创建串行队列(给定名字+指定队列类型)
  21. NSLog(@"开始执行啦:%@", [NSThread currentThread]);
  22. dispatch_queue_t queue = dispatch_queue_create("FirstSerialQueue", DISPATCH_QUEUE_SERIAL);
  23. //2.添加两个任务到串行队列中(block)
  24. //3.同步执行两个任务
  25. dispatch_sync(queue, ^{
  26. //添加第一个任务(耗时操作)
  27. for(int i = 0; i < 5; i++){
  28. [NSThread sleepForTimeInterval:1];
  29. NSLog(@"+++++++%@", [NSThread currentThread]);
  30. }
  31. });
  32. NSLog(@"打印+结束");
  33. dispatch_sync(queue, ^{
  34. for (int i = 0; i < 5; i++) {
  35. [NSThread sleepForTimeInterval:1];
  36. NSLog(@"--------%@", [NSThread currentThread]);
  37. }
  38. });
  39. NSLog(@"打印-结束");
  40. }
  41. /**串行队列异步执行*/
  42. - (IBAction)serialQueueAsync:(id)sender {
  43. /*
  44. 串行队列:顺序地执行
  45. 异步执行:当前线程 + 等待任务执行完毕
  46. */
  47. dispatch_queue_t queue = dispatch_queue_create("SecondSerialQueue", DISPATCH_QUEUE_SERIAL);
  48. dispatch_async(queue, ^{
  49. //添加到队列中的任务
  50. for (int i = 0; i < 5; i++) {
  51. [NSThread sleepForTimeInterval:1];
  52. NSLog(@"++++++%@", [NSThread currentThread]);
  53. }
  54. });
  55. NSLog(@"打印+完毕");
  56. dispatch_async(queue, ^{
  57. for (int i = 0; i < 5; i++) {
  58. [NSThread sleepForTimeInterval:1];
  59. NSLog(@"-------%@", [NSThread currentThread]);
  60. }
  61. });
  62. NSLog(@"打印-完毕");
  63. }
  64. /**并行队列同步执行*/
  65. - (IBAction)concurrentQueueSync:(id)sender {
  66. /*
  67. 并行队列:同时地执行
  68. 同步执行:当前线程 + 等待任务执行完毕
  69. */
  70. dispatch_queue_t queue = dispatch_queue_create("FirstConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
  71. dispatch_sync(queue, ^{
  72. for (int i = 0; i < 5; i++) {
  73. [NSThread sleepForTimeInterval:1];
  74. NSLog(@"+++++++%@", [NSThread currentThread]);
  75. }
  76. });
  77. NSLog(@"打印+完毕");
  78. dispatch_sync(queue, ^{
  79. for (int i = 0; i < 5; i++) {
  80. [NSThread sleepForTimeInterval:1];
  81. NSLog(@"-------%@", [NSThread currentThread]);
  82. }
  83. });
  84. NSLog(@"打印-完毕");
  85. }
  86. /**并行队列异步执行*/
  87. - (IBAction)concurrentQueueAsync:(id)sender {
  88. /*
  89. 并行队列:同时地执行
  90. 异步执行:子线程 + 立即返回(不等待任务)
  91. */
  92. dispatch_queue_t queue = dispatch_queue_create("SecondConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
  93. dispatch_async(queue, ^{
  94. for (int i = 0; i < 5; i++) {
  95. [NSThread sleepForTimeInterval:1];
  96. NSLog(@"+++++++%@", [NSThread currentThread]);
  97. }
  98. });
  99. NSLog(@"打印+完毕");
  100. dispatch_async(queue, ^{
  101. for (int i = 0; i < 5; i++) {
  102. [NSThread sleepForTimeInterval:1];
  103. NSLog(@"---------%@", [NSThread currentThread]);
  104. }
  105. });
  106. NSLog(@"打印-完毕");
  107. }
  108. /**全局队列异步执行*/
  109. - (IBAction)globalQueueAsync:(id)sender {
  110. //结论和并行队列异步执行一样
  111. //1.获取全局队列(只有这一步不一样)
  112. /*参数一:指定全局队列的优先级(主队列优先级最高)*/
  113. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  114. //2.添加任务
  115. dispatch_async(queue, ^{
  116. NSLog(@"+++++++%@", [NSThread currentThread]);
  117. });
  118. //3.异步执行任务
  119. NSLog(@"打印+完毕");
  120. }
  121. /**主队列异步执行*/
  122. - (IBAction)mainQueueAsync:(id)sender {
  123. //1.获取主队列
  124. dispatch_queue_t queue = dispatch_get_main_queue();
  125. //2.添加到主队列
  126. dispatch_async(queue, ^{
  127. for (int i = 0; i < 5; i++) {
  128. [NSThread sleepForTimeInterval:1];
  129. NSLog(@"--------%@", [NSThread currentThread]);
  130. }
  131. });
  132. NSLog(@"打印-完毕");
  133. }
  134. /**主队列同步执行*/
  135. - (IBAction)mainQueueSync:(id)sender {
  136. // dispatch_queue_t queue = dispatch_get_main_queue();
  137. NSLog(@"任务一");
  138. dispatch_sync(dispatch_get_main_queue(), ^{
  139. NSLog(@"任务二");
  140. });
  141. NSLog(@"任务三");
  142. }
  143. </span></span>

分析:

不用的组合:

1. 并行队列同步执行

   a. 前提:当前线程是主线程的时候,不会达到同时执行任务的目的

2.一般都不使用同步执行任务;原因是会阻塞当前线程

3.主队列同步执行(死锁)



GCD底层实现原理:

1. 底层维护一个线程池,自动从线程池中找空闲的子线程

2. 不需要创建子线程对象;


Demo03_GCD_DownloadImage

  1. <span style="font-size:14px;">- (IBAction)downloadImageByGCD:(id)sender {
  2. //选择组合:全局队列异步执行 -> 由子线程下载图片
  3. dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
  4. dispatch_async(globalQueue, ^{
  5. NSLog(@"开始下载图片:%@", [NSThread currentThread]);
  6. //NSString -> NSURL -> NSData -> UIImage
  7. NSString *imageStr = @"http://www.egouz.com/uploadfile/2015/0305/20150305103626911.jpg";
  8. NSURL *imageURL = [NSURL URLWithString:imageStr];
  9. //下载图片
  10. NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
  11. UIImage *image = [UIImage imageWithData:imageData];
  12. //从子线程回到主线程(方式二:常用)
  13. //组合:主队列异步执行
  14. dispatch_async(dispatch_get_main_queue(), ^{
  15. NSLog(@"回到主线程:%@", [NSThread currentThread]);
  16. //更新界面
  17. self.imageView.image = image;
  18. });
  19. NSLog(@"xxxxxxxx");
  20. });
  21. //主线程执行
  22. NSLog(@"下载图片。。。。。");
  23. }
  24. </span>



声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/387187
推荐阅读
相关标签
  

闽ICP备14008679号