面试题答案
一键面试可能出现的问题
- 数据竞争:多个线程同时尝试发送或接收消息,可能导致数据混乱。例如,发送缓冲区可能会被多个线程同时写入,接收的数据可能因为多个线程同时处理而丢失或重复处理。
- 连接状态混乱:不同线程对WebSocket连接状态的判断和修改可能不一致。比如一个线程正在关闭连接,而另一个线程尝试发送消息,这可能导致未定义行为。
- 资源争用:WebSocket底层的网络资源(如套接字)可能被多个线程同时争夺,造成资源耗尽或性能下降。
解决方法
- 锁机制:
- 互斥锁(Mutex):在发送和接收消息的关键代码段前后加锁。例如,使用
NSLock
类:
- 互斥锁(Mutex):在发送和接收消息的关键代码段前后加锁。例如,使用
NSLock *socketLock = [[NSLock alloc] init];
// 发送消息
[socketLock lock];
// 发送消息的代码,如 [webSocket send:message];
[socketLock unlock];
- **读写锁(Read - Write Lock)**:如果有大量的读操作(接收消息)和少量的写操作(发送消息),可以使用读写锁。在Objective - C中没有标准的读写锁类,但可以通过组合`NSCondition`和`NSConditionLock`来实现。读操作可以多个线程同时进行,而写操作时需要独占锁。
2. 队列机制:
- 操作队列(NSOperationQueue):将所有的WebSocket相关操作(发送、接收、连接管理等)封装成NSOperation
对象,然后添加到一个NSOperationQueue
中。这样所有操作会按顺序执行,避免了多线程并发问题。
NSOperationQueue *socketQueue = [[NSOperationQueue alloc] init];
socketQueue.maxConcurrentOperationCount = 1; // 确保顺序执行
NSBlockOperation *sendOperation = [NSBlockOperation blockOperationWithBlock:^{
// 发送消息的代码,如 [webSocket send:message];
}];
[socketQueue addOperation:sendOperation];
- **GCD队列**:使用Grand Central Dispatch(GCD)的串行队列。可以创建一个全局的串行队列或一个自定义的串行队列来处理WebSocket操作。
dispatch_queue_t socketQueue = dispatch_queue_create("com.example.websocket.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(socketQueue, ^{
// 发送消息的代码,如 [webSocket send:message];
});
- 原子操作和属性:将WebSocket连接对象的关键属性声明为原子(atomic)。例如,如果有一个表示连接状态的属性
connected
,声明为@property (atomic, assign, readonly) BOOL connected;
,这样在多线程访问该属性时会保证其原子性,避免数据竞争。但需要注意,原子属性只能保证单个属性访问的原子性,对于复杂的操作序列,还是需要其他同步机制。 - 状态机:实现一个状态机来管理WebSocket的连接状态。不同线程在进行操作前先检查当前状态,确保操作符合当前状态。例如,只有在连接状态为
connected
时才能发送消息。这样可以避免因状态混乱导致的问题。