技术标签: 资源竞争 多线程实现 线程死锁 iOS高级进阶 线程安全 线程池
方案 | 简介 | 语言 | 线程生命周期 | 使用频率 |
---|---|---|---|---|
pthread | 一套通用的线程API,适用于Unix/Linux/Window等系,跨平台,可移植,使用难度大 | C | 程序员管理 | 几乎不用 |
NSThread | 使用更加面向对象,可直接操作线程对象 | OC | 程序员管理 | 偶尔使用 |
GCD | 旨在代替NSThread等线程技术,充分利用设备的多核 | C | 自动管理 | 经常使用 |
NSOperation | 基于GCD,比GCD多了部分更加简单实用的工能,使用更加面向对象 | OC | 自动管理 | 经常使用 |
pthread_t threadId = NULL;
// c字符串
char *cString = "HelloCode";
/**
pthread_create 创建线程
参数:
1. pthread_t:要创建线程的结构体指针,通常开发的时候,如果遇到 C 语言的结构体,类型后缀 `_t / Ref` 结尾
同时不需要 `*`
2. 线程的属性,nil(空对象 - OC 使用的) / NULL(空地址,0 C 使用的)
3. 线程要执行的`函数地址`
void *: 返回类型,表示指向任意对象的指针,和 OC 中的 id 类似
(*): 函数名
(void *): 参数类型,void *
4. 传递给第三个参数(函数)的`参数`
*/
int result = pthread_create(&threadId, NULL, pthreadTest, cString);
if (result == 0) {
NSLog(@"成功");
} else {
NSLog(@"失败");
}
// 创建
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil];
// 启动
[thread start];
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];
[self performSelectorInBackground:@selector(run:) withObject:nil];
@property (class, readonly, strong) NSThread *currentThread;
+ (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
+ (BOOL)isMultiThreaded;
@property (readonly, retain) NSMutableDictionary *threadDictionary;
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
+ (void)exit;
+ (double)threadPriority;
+ (BOOL)setThreadPriority:(double)p;
@property double threadPriority API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)); // To be deprecated; use qualityOfService below
@property NSQualityOfService qualityOfService API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0)); // read-only after the thread is started
@property (class, readonly, copy) NSArray<NSNumber *> *callStackReturnAddresses API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@property (class, readonly, copy) NSArray<NSString *> *callStackSymbols API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@property NSUInteger stackSize API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@property (readonly) BOOL isMainThread API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@property (class, readonly) BOOL isMainThread API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)); // reports whether current thread is main
@property (class, readonly, strong) NSThread *mainThread API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
- (instancetype)init API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
- (instancetype)initWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
@property (readonly, getter=isExecuting) BOOL executing API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@property (readonly, getter=isFinished) BOOL finished API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@property (readonly, getter=isCancelled) BOOL cancelled API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
- (void)cancel API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
- (void)start API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
- (void)main API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)); // thread body method
// 创建NSInvocationOperation对象
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
// 开始执行
[operation start];
// 创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
// 开始任务
[operation start];
// 创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
// 添加多个Block
for (int i = 0; i < 5; i++) {
[operation addExecutionBlock:^{
NSLog(@"第%d次:%@", i, [NSThread currentThread]);
}];
}
// 开始任务
[operation start];
2021-03-23 21:43:11.967090+0800 多线程[32490:7176005] 第4次:<NSThread: 0x6000013fb500>{
number = 7, name = (null)}
2021-03-23 21:43:11.967090+0800 多线程[32490:7176006] 第0次:<NSThread: 0x600001398400>{
number = 4, name = (null)}
2021-03-23 21:43:11.967090+0800 多线程[32490:7176013] 第1次:<NSThread: 0x600001398980>{
number = 5, name = (null)}
2021-03-23 21:43:11.967090+0800 多线程[32490:7175868] <NSThread: 0x6000013cc780>{
number = 1, name = main}
2021-03-23 21:43:11.967099+0800 多线程[32490:7176004] 第2次:<NSThread: 0x600001386e00>{
number = 6, name = (null)}
2021-03-23 21:43:11.967102+0800 多线程[32490:7176008] 第3次:<NSThread: 0x60000138b040>{
number = 3, name = (null)}
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSBlockOperation addExecutionBlock:]: blocks cannot be added after the operation has started executing or finished'
NSOperationQueue *queue = [NSOperationQueue mainQueue];
// 创建一个其他队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
// 添加多个Block
for (int i = 0; i < 5; i++) {
[operation addExecutionBlock:^{
NSLog(@"第%d次:%@", i, [NSThread currentThread]);
}];
}
// 队列添加任务
[queue addOperation:operation];
当多个线程同时访问一块资源时,容易引发数据错乱和数据安全问题,这个时候就需要互斥锁(即同步锁)和自旋锁来解决了。
@property (nonatomic, strong) NSString *target;
dispatch_queue_t queue = dispatch_queue_create("parallel", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 1000000 ; i++) {
dispatch_async(queue, ^{
self.target = [NSString stringWithFormat:@"%d",i];
});
}
任何事情都有两面性,就像多线程能提升效率的同时,也会造成资源竞争的问题。而锁在保证多线程的数据安全的同时,粗心大意之下也容易发生问题,那就是死锁。
NSOperationQueue
NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"lock 1 start");
[NSThread sleepForTimeInterval:1];
NSLog(@"lock 1 over");
}];
NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"lock 2 start");
[NSThread sleepForTimeInterval:1];
NSLog(@"lock 2 over");
}];
NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"lock 3 start");
[NSThread sleepForTimeInterval:1];
NSLog(@"lock 3 over");
}];
// 循环从属
[blockOperation2 addDependency:blockOperation1];
[blockOperation3 addDependency:blockOperation2];
[blockOperation1 addDependency:blockOperation3];
// 互相从属
[blockOperation1 addDependency:blockOperation2];
[blockOperation2 addDependency:blockOperation1];
NSOperationQueue *operationQueue = [NSOperationQueue mainQueue];
NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"lock 1 start");
[NSThread sleepForTimeInterval:1];
NSLog(@"lock 1 over");
}];
NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"lock 2 start");
[NSThread sleepForTimeInterval:1];
NSLog(@"lock 2 over");
}];
NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"lock 3 start");
[NSThread sleepForTimeInterval:1];
NSLog(@"lock 3 over");
}];
[operationQueue addOperation:blockOperation1];
[operationQueue addOperation:blockOperation2];
[operationQueue addOperation:blockOperation3];
GCD
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"task start");
[NSThread sleepForTimeInterval:1.0];
NSLog(@"task over");
});
dispatch_queue_t queue = dispatch_queue_create("sync", DISPATCH_QUEUE_SERIAL);
// 此处异步同样会造成互相等待
dispatch_sync(queue, ^{
NSLog(@"task 1 start");
dispatch_sync(queue, ^{
NSLog(@"task 2 start");
[NSThread sleepForTimeInterval:1.0];
NSLog(@"task 2 over");
});
NSLog(@"task 1 over");
});
dispatch_queue_t queue = dispatch_queue_create("asyn", DISPATCH_QUEUE_SERIAL);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(queue, ^{
__block NSString *str = @"YDW"; // 线程1 创建数据
dispatch_async(queue, ^{
str = [NSString stringWithFormat:@"%ld",[str hash]]; // 线程2 加工数据
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"%@",str); // 线程1 使用加工后的数据
});
if (!_lock) _lock = [NSLock new];
dispatch_queue_t queue = dispatch_queue_create("sync", DISPATCH_QUEUE_CONCURRENT);
[_lock lock];
dispatch_sync(queue, ^{
[_lock lock];
[NSThread sleepForTimeInterval:1.0];
[_lock unlock];
});
[_lock unlock];
主线程环境中,在主队列上执行同步任务,为什么会死锁?
// 任务1
// do something in task1
// 主线程环境中
dispatch_sync(dispatch_get_main_queue(), ^{
// 任务2
// do something in task2
});
主线程环境中,为什么在新创建的串行队列中执行同步任务就不会死锁?
// 任务1
// do something in task1
// 主线程环境中
dispatch_queue_t queue = dispatch_queue_create("YDW", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
// 任务2
// do something in task2
});
视频压缩中,每帧都代表着一幅静止的图像。而在进行实际压缩时,会采用各种算法以减少数据容量,其中IPB帧就是最常见的一种。 I帧:镇内编码帧(intra picture) I帧通常是每个GOP(MPEG所使用的一种视频研所技术)的第一帧,经过适度地压缩,作为随机访问的参考点,可以当成静态图像。 p帧:前向预测编码帧(predicive-frame),通过将图像序列中前面已编码帧的时间冗余信息从分去除来压缩传输数据量的编码图像,也称为预测帧。 B帧:双向预测...
WJMZBMR说过:“连POI都不会还搞个屁OI”,早就听说过POI,然而一直没有勇气去刷。几个月前才发现,POI的题目几乎都是趣味性很强的思维题。初学者如果要锻炼思维的话可以去刷一刷。由于NOI吧里都可以找到资源,所以我在这里就简要讲一下我做了的题目里面比较经典的思路和细节(不要怪博主懒,明天就去NOI了,怎么也赶不赢啊。。。。。)(题目序号都是bzoj上的)1097: [POI2007
exFatwindows 8 和 mac 都支持 exFat。NTFS只适用于windows,mac不支持,安装插件也不方便。网上有教程说新的mac系统可能通过配置,支持NTFS,但我的系统无法配置。HFS, mac 是可以使用的,但windows就不行。FAT mac 和 windows 都支持,但单文件最大只支持4G,并且对于1T的移动硬盘,支持也不好。—————————...
【 前言 】AVR单片机C语言编译软件有IAR、ICCAVR、winavr、Atmanavr、CodeVisionAVR等。其中CodeVisionAVR又称 CVAVR,它可以说是目前最容易入门的C编译器,它体积小巧,但是代码效率却很高, 风格与keil-C51类似,与Keil-C51一样支持位变量、支持IO端口的位操作(其它的如IAR、ICCAVR、GCCAVR都没有此功能)下面就以CodeV
菜鸡记录下ntfs格式的U盘挂载命令:安装ntfs-3gsudo yum list | grep ntfsntfs-3g.x86_64 2:2017.3.23-11.el7 epel ntfs-3g-devel.x86_64 2:2017.3.23-11.el7...
哈喽!大家好,我是「奇点」,江湖人称 singularity。刚工作几年,想和大家一同进步一位上进心十足的【Java ToB端大厂领域博主】!喜欢java和python,平时比较懒,能用程序解决的坚决不手动解决 如果有对【java】感兴趣的【小可爱】,欢迎关注我️️️。
Hibernate占位符?和:及JPA占位符小结一下hibernate占位符注意小结一下hibernate占位符最常见的?占位符,idea会有红色波浪线,但是可以正常运行,下标从0开始,这个让人头疼的是数?个数…String hql = "select a from Apple a where a.color=? a.weight>?";Query query = session.createQuery(hql);query.setParameter(0, "red");query.s
ORA-00257故障处理[[email protected]~]$sqlplusXX_js/XX_jsSQL*Plus:Release10.2.0.1.0-ProductiononMonNov815:25:402010Copyright(c)1982,2005,Oracle.Allrightsreserved.ERROR:ORA-00257:archivererror.Connectinternalo...
要求: 完成一个java application应用程序,来仿真2个生产者和3个消费者共同操作一个数据队列(例如一个vector类对象),数据队列中有时是没有数据的,这个时候消费者应该处于等待状态而不是不断的去访问这个数据队列。消费者线程一次消费后发现数据队列空了,就应该处于等待状态,生产者生产数据(向数据队列加入一条数据)后,就去唤醒消费者线程开始消费。生产者线程某次生产后发现数据队列已经满...
1、选择合适的算法和数据结构 应该熟悉算法语言,知道各种算法的优缺点,具体资料请参见相应的参考资料,有很多计算机书籍上都有介绍。将比较慢的顺序查找法用较快的二分查找或乱序查找法代替,插入排序或冒泡排序法用快速排序、合并排序或根排序代替,都可以大大提高程序执行的效率。.选择一种合适的数据结构也很重要,比如你在一堆随机存放的数中使用了大量的插入和删除指令,那使用链表要快得多。数组与指针语句具
AES-CMACAES-CMAC使用了高级加密标准作为组分。为了产生一个消息认证码,CMAC需要一个密钥,消息message及消息的长度length作为输入,输出是消息认证码。 AES-CMAC的核心是CBC-MAC。对于待加密消息M,应用CBC-MAC算法。在CMAC操作中有两种情况: 如果输入消息长度等于Block的整数倍,最后的Block M_n需要先于K1异或再进行处理; 如果输...