全部博文(1493)
分类:
2012-11-20 16:17:16
原文地址:·iOS多线程开发(四)---线程同步 作者:shoela950
Table 4-1 Lock types
Lock |
Description |
Mutex [互斥锁] |
A mutually exclusive (or mutex) lock acts as a protective barrier around a resource. A mutex is a type of semaphore that grants access to only one thread at a time. If a mutex is in use and another thread tries to acquire it, that thread blocks until the mutex is released by its original holder. If multiple threads compete for the same mutex, only one at a time is allowed access to it. |
Recursive lock [递归锁] |
A recursive lock is a variant on the mutex lock. A recursive lock allows a single thread to acquire the lock multiple times before releasing it. Other threads remain blocked until the owner of the lock releases the lock the same number of times it acquired it. Recursive locks are used during recursive iterations primarily but may also be used in cases where multiple methods each need to acquire the lock separately. |
Read-write lock |
A read-write lock is also referred to as a shared-exclusive lock. This type of lock is typically used in larger-scale operations and can significantly improve performance if the protected data structure is read frequently and modified only occasionally. During normal operation, multiple readers can access the data structure simultaneously. When a thread wants to write to the structure, though, it blocks until all readers release the lock, at which point it acquires the lock and can update the structure. While a writing thread is waiting for the lock, new reader threads block until the writing thread is finished. The system supports read-write locks using POSIX threads only. For more information on how to use these locks, see the pthread man page. |
Distributed lock |
A distributed lock provides mutually exclusive access at the process level. Unlike a true mutex, a distributed lock does not block a process or prevent it from running. It simply reports when the lock is busy and lets the process decide how to proceed. |
Spin lock |
A spin lock polls its lock condition repeatedly until that condition becomes true. Spin locks are most often used on multiprocessor systems where the expected wait time for a lock is small. In these situations, it is often more efficient to poll than to block the thread, which involves a context switch and the updating of thread data structures. The system does not provide any implementations of spin locks because of their polling nature, but you can easily implement them in specific situations. For information on implementing spin locks in the kernel, see Kernel Programming Guide. |
Double-checked lock |
A double-checked lock is an attempt to reduce the overhead of taking a lock by testing the locking criteria prior to taking the lock. Because double-checked locks are potentially unsafe, the system does not provide explicit support for them and their use is discouraged.[注意系统不显式支持该锁类型] |
Table 4-2 Mutex and atomic operation costs
Item |
Approximate cost |
Notes |
Mutex acquisition time |
Approximately 0.2 microseconds |
This is the lock acquisition time in an uncontested case. If the lock is held by another thread, the acquisition time can be much greater. The figures were determined by analyzing the mean and median values generated during mutex acquisition on an Intel-based iMac with a 2 GHz Core Duo processor and 1 GB of RAM running Mac OS X v10.5. |
Atomic compare-and-swap |
Approximately 0.05 microseconds |
This is the compare-and-swap time in an uncontested case. The figures were determined by analyzing the mean and median values for the operation and were generated on an Intel-based iMac with a 2 GHz Core Duo processor and 1 GB of RAM running Mac OS X v10.5. |
NSLock* arrayLock = [self GetArrayLock];
NSMutableArray* myArray = GetSharedArray();
id anObject;
[arrayLock lock];
anObject = [myArray objectAtIndex:0];
[arrayLock unlock];
// 在处理doSomething的时候共享资源myArray可能被修改,下面的操作就是有风险的操作
[anObject doSomething];
NSMutableArray* myArray = GetSharedArray();
id anObject;
[arrayLock lock];
anObject = [myArray objectAtIndex:0];
// 在处理doSomething放到Lock里面,如果doSomething处理时间比较长,那么就形成了效率瓶颈,影响程序效率
[anObject doSomething];
[arrayLock unlock];
NSLock* arrayLock = [self GetArrayLock];
NSMutableArray* myArray = GetSharedArray();
id anObject;
[arrayLock lock];
anObject = [myArray objectAtIndex:0];
// 把对象retain and save,防止在unlock,myArray里面的内容被修改
[anObject retain];
[arrayLock unlock];
[anObject doSomething];
[anObject release];
4,当心死锁(DeadLock)和活锁(LiveLock)
任何线程试图同时获得多于一个锁,就有可能发生死锁的可能。当两个线程分别保持一个锁,A线程-A锁/B线程-B锁
避免死锁和活锁的最好方法是同一时间只拥有一个锁
5,正确使用volatile变量
关键字volatile只确保每次获取volatile变量时都是从内存加载变量,而不是使用寄存器里面的值,他不保证代码访问变量是正确的
Table 4-3 Atomic math and logic operations
Operation |
Function name |
Description |
Add |
OSAtomicAdd32 |
Adds two integer values together and stores the result in one of the specified variables. |
Increment |
OSAtomicIncrement32 |
Increments the specified integer value by 1. |
Decrement |
OSAtomicDecrement32 |
Decrements the specified integer value by 1. |
Logical OR |
Performs a logical OR between the specified 32-bit value and a 32-bit mask. | |
Logical AND |
Performs a logical AND between the specified 32-bit value and a 32-bit mask. | |
Logical XOR |
Performs a logical XOR between the specified 32-bit value and a 32-bit mask. | |
Compare and swap |
OSAtomicCompareAndSwap32 |
Compares a variable against the specified old value. If the two values are equal, this function assigns the specified new value to the variable; otherwise, it does nothing. The comparison and assignment are done as one atomic operation and the function returns a Boolean value indicating whether the swap actually occurred. |
Test and set |
Tests a bit in the specified variable, sets that bit to 1, and returns the value of the old bit as a Boolean value. Bits are tested according to the formula (0×80 >> (n & 7)) of byte((char*)address + (n >> 3)) where n is the bit number and address is a pointer to the variable. This formula effectively breaks up the variable into 8-bit sized chunks and orders the bits in each chunk in reverse. For example, to test the lowest-order bit (bit 0) of a 32-bit integer, you would actually specify 7 for the bit number; similarly, to test the highest order bit (bit 32), you would specify 24 for the bit number. | |
Test and clear |
Tests a bit in the specified variable, sets that bit to 0, and returns the value of the old bit as a Boolean value. Bits are tested according to the formula (0×80 >> (n & 7)) of byte((char*)address + (n >> 3)) where n is the bit number and address is a pointer to the variable. This formula effectively breaks up the variable into 8-bit sized chunks and orders the bits in each chunk in reverse. For example, to test the lowest-order bit (bit 0) of a 32-bit integer, you would actually specify 7 for the bit number; similarly, to test the highest order bit (bit 32), you would specify 24 for the bit number. |
// Listing 4-2 Using a mutex lock
void MyMutexInitFunction()
{
pthread_mutex_init(&mutex, NULL);
}
void MyLockingFunction()
{
// Lock the mutex
pthread_mutex_lock(&mutex);
// Do real the work
// unlock the mutex
pthread_mutex_unlock(&mutex);
}
2,使用NSLock类
Cocoa中所有的锁的接口实际上都是通过NSLocking协议定义的,除了定义了lock/unlock方法外,还定义了tryLock/lockBeforeDate:方法。tryLock方法试图获取一个锁,如果所不可用,他不会阻塞线程,相反,他只返回NO。lockBeforeDate:方法试图获取一个锁,如果锁没有在规定的时间内被获取到,他会让线程,从阻塞状态变为非阻塞状态(或者返回NO)。
- (void)testUsingLock
{
BOOL moreToDo = NO;
NSLock *theLock = [[NSLock alloc] init];
while(moreToDo)
{
/* Do another increment of calculation */
/* until there's no more to do. */
if([theLock tryLock])
{
/* Update the display used by all threads */
[theLock unlock];
}
}
}
3,使用@synchronized指令
@synchronized指令做和其他互斥锁一样的工作(防止不同线程在同一时间获取同一个锁)
- (void)MyMethod:(id)anObj
{
@synchronized(anObj)
{
// Everything between the braces is protected by the @synchronized directive.
}
}
创建@synchronized(anObj)指令的对象是一个用来区别保护块的唯一标示符,如果在不同线程调用上述方法,如果每次在线程传递的不同的对象给anObj,那么每次他都将拥有他的锁,并持续处理,而不被其他线程阻塞。如果传递的是同一参数,那么该线程被阻塞,直到前一个线程解锁后,才能继续处理
另,@synchronized()块隐式的添加一个异常处理例程来保护代码,所以需要在程序中启用异常处理。
4,使用其他锁
1)使用NSRecursiveLock对象---递归锁
对同一线程,可以多次获得(lock)而不会造成死锁,注意的是lock/unlock需要配对出现
NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init];
- (void)MyRecursiveLockFunction:(int)value
{
[theLock lock];
if(value != 0)
{
-- value;
[self MyRecursiveLockFunction:value];
}
[theLock unlock];
}
2)使用NSConditionLock对象---条件锁
条件锁是一个互斥锁,可以通过特定值来锁住和解锁。
NSConditionLock的锁住和解锁的方法,unlockWithCondition:和lock消息,或者lockWhenCondition:和unlock消息。两对可以任意组合使用
===生产者-消费者问题
#define NO_DATA 0
#define HAS_DATA 1
isEmpty = NO;
NSConditionLock *condLock = [[NSConditionLock alloc] initWithCondition:NO_DATA];
// 生产者线程数据处理流程
- (void)MyConditionLockPruductor
{
while(true)
{
[condLock lock];
/* Add data to the queue */
[condLock unlockWithCondition:HAS_DATA];
}
}
// 消费者线程数据处理流程
- (void)MyConditionLockCustomer
{
while(true)
{
[condLock lockWhenCondition:HAS_DATA];
/* remove data from the queue */
[condLock unlockWithCondition:(isEmpty?NO_DATA:HAS_DATA)];
// Process the data locally.
}
}
3)使用NSDistributedLock对象---分布锁
用于协调多台主机访问某系共享资源,比如一个文件/一个目录等。和其他lock锁的不同,NSDistributedLock对象并没有实现NSLocking协议,所以他没有lock方法。不过提供了一个tryLock方法,可以通过breadLock方法打破现在锁。
// Listing 4-3 Using a Cocoa condition
- (void)testUsingConditoin
{
[cocaoCondition lock];
while(timeToDoWork <= 0)
{
[cocaoCondition wait];
}
-- timeToDoWork;
// Do real work here
[cocaoCondition unlock];
}
// Listing 4-4 Signaling a Cocoa condition
- (void)signalCocaoConditon
{
[cocaoCondition lock];
++ timeToDoWork;
[cocaoCondition signal];
[cocaoCondition unlock];
}
pthread_mutex_t mutex;
pthread_cond_t condition;
Boolean ready_to_go = true;
void MyCondInitFunction()
{
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&condition, NULL);
}
void MyWaitOnConditionFunction()
{
// Lock the mutex
pthread_mutex_lock(&mutex);
// if the predicate is already set, then the while loop is bypassed;
// otherwise, the thread sleeps until the predicate is set
while(ready_to_go == false)
{//这个while要特别说明一下,单个pthread_cond_wait功能很完善,为何这里要有一个while (head == NULL)呢?因为pthread_cond_wait里的线程可能会被意外唤醒,如果这个时候head != NULL,则不是我们想要的情况。这个时候,应该让线程继续进入pthread_cond_wait
pthread_cond_wait(&condition, &mutex);// pthread_cond_wait会先解除之前的pthread_mutex_lock锁定的mutex,然后阻塞在等待对列里休眠,直到再次被唤醒(大多数情况下是等待的条件成立而被唤醒,唤醒后,该进程会先锁定先pthread_mutex_lock(&mutex);,再读取资源
//用这个流程是比较清楚的 /*block-->unlock-->wait() return-->lock*/
}
// Do work. (The mutex should stay locked.)
// Reset the predicate and release the mutex.
ready_to_go = false;
pthread_mutex_unlock(&mutex);
}
===使用POSIX条件,通知解锁
// Listing 4-6 Signaling a condition lock
void signalThreadUsingCondition()
{
// At the point, there should be work for the other thread to do
pthread_mutex_lock(&mutex);
ready_to_go = true;
// signal the other thread to begin work
pthread_cond_signal(&condition);
pthread_mutex_unlock(&mutex);
}
术语表
应用(application) 一个显示一个图形用户界面给用户的特定样式程序。
条件(condition) 一个用来同步资源访问的结构。线程等待某一条件来决定是否被允许继续运行,直到其他线程显式的给该条件发送信号。
临界区(critical section) 同一时间只能不被一个线程执行的代码。
输入源(input source) 一个线程的异步事件源。输入源可以是基于端口的或手工触发,并且必须被附加到某一个线程的 run loop 上面。
可连接的线程(join thread) 退出时资源不会被立即回收的线程。可连接的线程在资源被回收之前必须被显式脱离或由其他线程连接。可连接线程供了一个返回值给连接它的线程。
主线程(main thread) 当创建进程时一起创建的特定类型的线程。当程序的主线程退出,则程序即退出。
互斥锁(mutex) 供共享资源互斥访问的锁。一个互斥锁同一时间只能被一个线程拥有。试图获取一个已经被其他线程拥有的互斥锁,会把当前线程置于休眠状态知道该锁被其他线程释放并让当前线程获得。
操作对象(operation object)
NSOperation 类的实例。操作对象封装了和某一任务相关的代码和数据到一个执行单元里面。
操作队列(operation queue)
NSOperationQueue 类的实例。操作队列管理操作对象的执行。
进程(process) 应用或程序的运行时实例。一个进程拥有独立于分配给其他程序的的内存空间和系统资源(包括端口权限)。进程总是包含至少一个线程(即主线程)和
任意数量的额外线程。
程序(program) 可以用来执行某些任务的代码和资源的组合。程序不需要一个图形用户界面, 尽管图形应用也被称为程序。
递归锁(recursive lock) 可以被同一线程多次锁住的锁。
Run loop(运行循环) 一个事件处理循环,在此期间事件被接收并分配给合适的处理例程。
Run loop 模式(run loop mode)
与某一特定名称相关的输入源、定时源和 run loop 观察者的集合。当运行在某一特定“模式”下,一个 run loop 监视和该模式相关的源和观察者。
Run loop 对象(run loop object)
NSRunLoop 类或 CFRunLoopRef 不透明类型的实例。这些对象供线程里面实现事件处理循环的接口。
Run loop 观察者(run loop observer)
在 run loop 运行的不同阶段时接收通知的对象。
信号量(semaphore) 一个受保护的变量,它限制共享资源的访问。互斥锁(mutexes)和条件 (conditions)都是不同类型的信号量。
任务(task)
要执行的工作数量。尽管一些技术(最显著的是 Carbon 多进程服务—Carbon Multiprocessing Services)使用该术语的意义有时不同,但是最通用的用法 是表明需要执行的工作数量的抽象概念。
线程(thread) 进程里面的一个执行过程流。每个线程都有它自己的栈空间,但除此之外同一进程的其他线程共享内存。
定时源(timer source) 为线程同步事件的源。定时器产生预定时间将要执行的一次或重复事件。