Objective-C在应用程序中支持多任务。这意味着可能有两个线程同时视图徐改同一个对象。有一个办法可以解决这个情况。为了防止多个线程同时执行同一个代码块,OC提供了@synchronized()指令。
使用@synchronized()指令可以锁住在线程中执行的某一个代码块。存在被保护(即被锁住)的代码块的其他线程,将被阻塞,这也就意味着,他们将在@synchronized()代码块的最后一条语句执行结束后才能继续执行。
@synchronized()指令的唯一参数可以使用任何OC对象,包括self。这个对象就是我们所谓的信号量。这个允许线程锁住一个代码块以防它被别的线程使用。你应当使用不同的信号量来保护程序中不同能够的关键部分。最安全的是,在应用程序进入多线程之前,为所有需要互斥的创建互斥信号量。
以下示例代码展示了一个使用self作为信号量来互斥同步访问当前对象的实例方法。你可以使用同样的方法来同步调用相关联类的类方法,使用类对象,而不是self。在后一种情况(即使用类对象调用类方法),一次只允许一个线程执行一个类方法,因为所有调用对象都使用同一个类对象。
EP1:
- (void)criticalMethod
{
@synchronized(self) {
// Critical code.
}
}
下面的示例使用了当前的selector(翻完了才发现应该是当前执行的方法),_cmd作为信号量。这种同步方式仅在被同步的方法具有唯一名字(即唯一标示符)时有用。在当前方法执行完毕是,不会有其他对象,或者类,会被允许执行一个同样名字的方法。
EP2:
- (void)criticalMethod
{
@synchronized(NSStringFromSelector(_cmd)) {
// Critical code. ...
}
}
下面的示例展示了一个最常用的方法。在执行一个临界线程之前,代码从类Account创建了一个信号量,并使用他锁住临界代码段。这个Account应当在它的initialize方法里面一个信号量。(其实我想说是对信号量初始化)
EP3:
Account *account = [Account accountFromString:[accountField stringValue]];
// Get the semaphore.
id accountSemaphore = [Account semaphore];
@synchronized(accountSemaphore) {
// Critical code. ...
}
OC的同步特性支持递归和重载方法。一个线程可以使用一个信号量多次,在一个递归的方法。其他线程将被阻塞直到该线程释放所有与他相关的锁。(我的理解就是信号量归零啦)也就是说,每一个@synchronized()块都正常退出或者抛给一个异常。
当代码在一个@synchronized()块中,掷出异常时,OC的运行环境捕获这个异常,释放掉这个信号量(此时被保护的代码可以被其他进程锁执行),然后再次抛掷异常到下一个异常处理。
EP1的测试代码:
- (void)testThread{
while (1) {
@synchronized(self){
i=i++;
NSLog(@"%i当前线程名称:%@",i,[[NSThread currentThread] name]);
}
}
}
- (void)startThread{
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(testThread)object:nil];
[thread1 setName:@"线程1"];
[thread1 start];
[thread1 release];
NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(testThread)object:nil];
[thread2 setName:@"线程2"];
[thread2 start];
[thread2 release];
NSThread *thread3 = [[NSThread alloc] initWithTarget:self selector:@selector(testThread)object:nil];
[thread3 setName:@"线程3"];
[thread3 start];
[thread3 release];
}
多个线程同时调用这段代码的时候,执行顺序严格按照线程开启的顺序执行。
下面是部分LOG的记录:
1当前线程名称:线程1
2当前线程名称:线程2
3当前线程名称:线程3
4当前线程名称:线程1
5当前线程名称:线程2
6当前线程名称:线程3
7当前线程名称:线程1
8当前线程名称:线程2
9当前线程名称:线程3
10当前线程名称:线程1
11当前线程名称:线程2
12当前线程名称:线程3
13当前线程名称:线程1
14当前线程名称:线程2
15当前线程名称:线程3
使用@synchronized()指令可以锁住在线程中执行的某一个代码块。存在被保护(即被锁住)的代码块的其他线程,将被阻塞,这也就意味着,他们将在@synchronized()代码块的最后一条语句执行结束后才能继续执行。
@synchronized()指令的唯一参数可以使用任何OC对象,包括self。这个对象就是我们所谓的信号量。这个允许线程锁住一个代码块以防它被别的线程使用。你应当使用不同的信号量来保护程序中不同能够的关键部分。最安全的是,在应用程序进入多线程之前,为所有需要互斥的创建互斥信号量。
以下示例代码展示了一个使用self作为信号量来互斥同步访问当前对象的实例方法。你可以使用同样的方法来同步调用相关联类的类方法,使用类对象,而不是self。在后一种情况(即使用类对象调用类方法),一次只允许一个线程执行一个类方法,因为所有调用对象都使用同一个类对象。
EP1:
- (void)criticalMethod
{
@synchronized(self) {
// Critical code.
}
}
下面的示例使用了当前的selector(翻完了才发现应该是当前执行的方法),_cmd作为信号量。这种同步方式仅在被同步的方法具有唯一名字(即唯一标示符)时有用。在当前方法执行完毕是,不会有其他对象,或者类,会被允许执行一个同样名字的方法。
EP2:
- (void)criticalMethod
{
@synchronized(NSStringFromSelector(_cmd)) {
// Critical code. ...
}
}
下面的示例展示了一个最常用的方法。在执行一个临界线程之前,代码从类Account创建了一个信号量,并使用他锁住临界代码段。这个Account应当在它的initialize方法里面一个信号量。(其实我想说是对信号量初始化)
EP3:
Account *account = [Account accountFromString:[accountField stringValue]];
// Get the semaphore.
id accountSemaphore = [Account semaphore];
@synchronized(accountSemaphore) {
// Critical code. ...
}
OC的同步特性支持递归和重载方法。一个线程可以使用一个信号量多次,在一个递归的方法。其他线程将被阻塞直到该线程释放所有与他相关的锁。(我的理解就是信号量归零啦)也就是说,每一个@synchronized()块都正常退出或者抛给一个异常。
当代码在一个@synchronized()块中,掷出异常时,OC的运行环境捕获这个异常,释放掉这个信号量(此时被保护的代码可以被其他进程锁执行),然后再次抛掷异常到下一个异常处理。
EP1的测试代码:
- (void)testThread{
while (1) {
@synchronized(self){
i=i++;
NSLog(@"%i当前线程名称:%@",i,[[NSThread currentThread] name]);
}
}
}
- (void)startThread{
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(testThread)object:nil];
[thread1 setName:@"线程1"];
[thread1 start];
[thread1 release];
NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(testThread)object:nil];
[thread2 setName:@"线程2"];
[thread2 start];
[thread2 release];
NSThread *thread3 = [[NSThread alloc] initWithTarget:self selector:@selector(testThread)object:nil];
[thread3 setName:@"线程3"];
[thread3 start];
[thread3 release];
}
多个线程同时调用这段代码的时候,执行顺序严格按照线程开启的顺序执行。
下面是部分LOG的记录:
1当前线程名称:线程1
2当前线程名称:线程2
3当前线程名称:线程3
4当前线程名称:线程1
5当前线程名称:线程2
6当前线程名称:线程3
7当前线程名称:线程1
8当前线程名称:线程2
9当前线程名称:线程3
10当前线程名称:线程1
11当前线程名称:线程2
12当前线程名称:线程3
13当前线程名称:线程1
14当前线程名称:线程2
15当前线程名称:线程3