面试题答案
一键面试处理线程间通信和共享数据的常见方法
- 锁机制
- 互斥锁(Mutex):通过互斥访问共享资源,同一时间只有一个线程能访问。例如
pthread_mutex_t
,初始化后在访问共享数据前加锁,访问后解锁。 - 自旋锁(Spin Lock):线程尝试获取锁时,如果锁不可用,它不会进入睡眠状态,而是在原地循环等待锁可用。适用于锁被持有时间较短的场景。
- 读写锁(Read - Write Lock):允许多个线程同时读共享资源,但只允许一个线程写。读操作时使用读锁,写操作时使用写锁。
- 互斥锁(Mutex):通过互斥访问共享资源,同一时间只有一个线程能访问。例如
- 信号量(Semaphore):用于控制同时访问共享资源的线程数量。可以设置信号量的初始值,线程获取信号量时,信号量值减1,释放信号量时,信号量值加1。当信号量值为0时,其他线程获取信号量会被阻塞。
- 条件变量(Condition Variable):常与互斥锁一起使用,线程在满足特定条件时才继续执行。例如,一个线程等待某个条件满足,另一个线程改变条件后通知等待的线程。
- 队列(Queue):使用队列来管理任务,如Grand Central Dispatch(GCD)的队列或NSOperationQueue。可以将任务添加到队列中,队列按照一定规则顺序执行任务,避免了多线程同时访问共享资源的冲突。
- 原子属性(Atomic Property):在属性声明时使用
atomic
关键字,编译器会生成线程安全的访问器方法。但注意,这只能保证属性的getter和setter方法是线程安全的,对于复杂的操作仍可能需要额外的同步机制。
使用NSThread类配合其他机制确保数据一致性和线程安全示例
#import <Foundation/Foundation.h>
// 定义共享数据
@interface SharedData : NSObject
@property (nonatomic, assign) int value;
@end
@implementation SharedData
@end
// 定义一个锁
static pthread_mutex_t mutex;
// 线程执行的函数
void *threadFunction(void *data) {
SharedData *shared = (__bridge SharedData *)data;
// 加锁
pthread_mutex_lock(&mutex);
for (int i = 0; i < 5; i++) {
shared.value++;
NSLog(@"Thread %@ incremented value to %d", [NSThread currentThread], shared.value);
}
// 解锁
pthread_mutex_unlock(&mutex);
return NULL;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 初始化锁
pthread_mutex_init(&mutex, NULL);
SharedData *sharedData = [[SharedData alloc] init];
sharedData.value = 0;
// 创建两个线程
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(threadFunction:) object:(__bridge id)(sharedData)];
NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(threadFunction:) object:(__bridge id)(sharedData)];
// 启动线程
[thread1 start];
[thread2 start];
// 等待线程执行完毕
[thread1 join];
[thread2 join];
// 销毁锁
pthread_mutex_destroy(&mutex);
NSLog(@"Final value: %d", sharedData.value);
}
return 0;
}
在上述示例中,我们定义了一个SharedData
类来表示共享数据,使用pthread_mutex_t
类型的互斥锁来确保多个线程对sharedData.value
的访问是线程安全的。每个线程在访问共享数据前加锁,访问完毕后解锁。这样就保证了数据的一致性和线程安全。