鸟人 发表于 2010-7-31 19:35:36

多线程编程技术

标题很诱惑,无奈自己完全看不懂,转过来给看得懂的人,希望有用
作者: libin1201119 (1 篇文章) 日期: 七月 20, 2010 在 9:13 上午

条件变量的地址

attr 初始化条件变量的 attr的地址返回: 0 –成功;非 0 --失败例子:下面这个例子,仅仅只是使用不同的方法,对几个条件变量进行了初始化,然后再销毁,并没有真正使用条件变量进行应用处理。

#define _MULTI_THREADED #include #include #include "check.h"

pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER; pthread_cond_t cond2;

pthread_cond_t cond3;

int main(int argc, char **argv)

{ int rc=0; pthread_condattr_t attr;

printf("Enter Testcase - %s\n", argv);

printf("Create the default cond attributes object\n"); rc = pthread_condattr_init(&attr); checkResults("pthread_condattr_init()\n", rc);

printf("Create the all of the default conditions in different ways\n"); rc = pthread_cond_init(&cond2, NULL); checkResults("pthread_cond_init()\n", rc);

rc = pthread_cond_init(&cond3, &attr);

checkResults("pthread_cond_init()\n", rc);

printf("- At this point, the conditions with default attributes\n");

printf("- Can be used from any threads that want to use them\n");

printf("Cleanup\n"); pthread_condattr_destroy(&attr); pthread_cond_destroy(&cond1); pthread_cond_destroy(&cond2); pthread_cond_destroy(&cond3);

printf("Main completed\n"); return 0;

}

10.2 pthread_cond_wait 语法: #include int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

功能:这个函数将会阻塞当前线程,等待条件产生(即某个线程发出信号)。当执行这个函数时,必须先 lock住指定的互斥体 mutex,在执行到 pthread_cond_wait

函数的语句时,将会对互斥体进行解锁操作,然后开始等待。当等待的条件满足,或者线程被 cancel了(线程被 cancel时,虽然不会再执行应用程

序的语句,但系统仍会就当前线程进行处理),在线程继续执行之前,当前线程将会首先自动获取对互斥体的所有权(即锁住互斥体)。如果之前,当前线程未对互斥体执行 lock操作,那么将会返回一个 EPERM的错误信息。如果此时别的线程锁住了互斥体,当前线程将处理一个等待对方对互斥体解锁的状态中。在一个时点上,只能对一个条件变量分配一个互斥体。在同一时间对一个条件变量分配两个互斥体将会导致应用程序出现无法预测的串行问题。

Pthread_cond_wait这个函数可以被取消。

基于条件变量的原理,为了确保发出信号时,不会失效(即 pthread_cond_wait操作在 pthread_cond_signal之后发生),最好使用一个布尔型变量进行判断参数:

cond

输入参数,条件变量的地址

mutex

输入参数,分配给条件变量的互斥体的地址返回: 0 –成功;非 0 –失败

例子:在下面这个例子中,程序的执行流程大致上是这样的:子线程 1锁住互斥体,然后执行 pthread_cond_wait,解锁,等待;子线程 2锁住互斥体,然后执行 pthread_cond_wait,解锁,等待;初始线程等两个子线程都开始执行 pthread_cond_wait(这里直接使用了 sleep来实现等

待的目的。原程序段中也说明了,sleep并不是一个很健壮的写法,这里只是举例)

然后锁住互斥体,更新关键数据 conditionMet(子线程根据这个值判断是否结果循环)发出广播,唤醒所有等待的线程然后初始线程对互斥体解锁(必须要解锁,不然子线程唤醒后重新获取互斥体的所有权,

即对互斥体进行锁操作,而此时互斥体又被初始线程锁住,然后初始线程又等待子线程结束,

于是形成死锁)子线程 1和子线程 2同时被唤醒子线程 1获得互斥体的所有权,继续向下执行;子线程 2在等待获取互斥体所有权。子线程 1判断循环的条件满足,退出循环,对互斥体解锁;子线程 2获取互斥体的所有权,也退出循环,对互斥体解锁。最后初始线程等到了两个子线程的操作,回收资源,程序结束。

#define _MULTI_THREADED #include

#include #define checkResults(string, val) { \

if(val){ \ printf("Failed with %d at %s", val, string); \ exit(1); \

}\ }

/* For safe condition variable usage, must use a boolean predicate and */ /* a mutex with the condition. */

int conditionMet = 0; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

#define NTHREADS 5

void *threadfunc(void *parm) { int rc;

rc = pthread_mutex_lock(&mutex); checkResults("pthread_mutex_lock()\n", rc);

while (!conditionMet) { printf("Thread blocked\n"); rc = pthread_cond_wait(&cond, &mutex); checkResults("pthread_cond_wait()\n", rc);

}

rc = pthread_mutex_unlock(&mutex); checkResults("pthread_mutex_lock()\n", rc); return NULL;

}

int main(int argc, char **argv)

{ int rc=0; int i; pthread_t threadid;

printf("Enter Testcase - %s\n", argv);

printf("Create %d threads\n", NTHREADS);

for(i=0; i<NTHREADS; ++i) { rc = pthread_create(&threadid, NULL, threadfunc, NULL); checkResults("pthread_create()\n", rc);

}

sleep(5); /* Sleep is not a very robust way to serialize threads */ rc = pthread_mutex_lock(&mutex); checkResults("pthread_mutex_lock()\n", rc);

/* The condition has occured. Set the flag and wake up any waiting threads */ conditionMet = 1; printf("Wake up all waiting threads...\n");

rc = pthread_cond_broadcast(&cond); checkResults("pthread_cond_broadcast()\n", rc);

rc = pthread_mutex_unlock(&mutex); checkResults("pthread_mutex_unlock()\n", rc);

printf("Wait for threads and cleanup\n");

for (i=0; i<NTHREADS; ++i) { rc = pthread_join(threadid, NULL); checkResults("pthread_join()\n", rc);

} pthread_cond_destroy(&cond); pthread_mutex_destroy(&mutex);

printf("Main completed\n"); return 0; }

10.3 pthread_cond_signal 语法:

#include int pthread_cond_signal(pthread_cond_t *cond);

功能:

发出信号,用来唤起至少一个之前挂起的线程(具体作用范围不详,可以认为就只能唤起一个线程?)。如果没有符合条件的线程,那么这个发出的信号将会作废,没有影响到任何处理。

在执行 pthread_cond_wait函数时,需要为条件变量分配一个互斥体。当前线程无论是否拥有互斥体(即是否锁住),都可以执行 pthread_cond_signal这个函数。当然,如果应用程序需要的话,也可以在调用 pthread_cond_signal这个函数之前锁住互斥体。参数:

cond

输入参数,条件变量的地址返回: 0 –成功;非 0 – 失败例子:

#define _MULTI_THREADED #include #include #define checkResults(string, val) { \

if(val){ \ printf("Failed with %d at %s", val, string); \ exit(1); \

}\

}

/* For safe condition variable usage, must use a boolean predicate and */ /* a mutex with the condition. */ int workToDo = 0; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

#define NTHREADS 2

void *threadfunc(void *parm) { int rc;

while (1) { /* Usually worker threads will loop on these operations */ rc = pthread_mutex_lock(&mutex); checkResults("pthread_mutex_lock()\n", rc);

while (!workToDo) { printf("Thread blocked\n"); rc = pthread_cond_wait(&cond, &mutex); checkResults("pthread_cond_wait()\n", rc);

} printf("Thread awake, finish work!\n");

/* Under protection of the lock, complete or remove the work */ /* from whatever worker queue we have. Here it is simply a flag */ workToDo = 0;

rc = pthread_mutex_unlock(&mutex);

checkResults("pthread_mutex_lock()\n", rc); } return NULL;

}

int main(int argc, char **argv)

{ int rc=0; int i; pthread_t threadid;

printf("Enter Testcase - %s\n", argv);

printf("Create %d threads\n", NTHREADS);

for(i=0; i<NTHREADS; ++i) { rc = pthread_create(&threadid, NULL, threadfunc, NULL); checkResults("pthread_create()\n", rc);

}

sleep(5); /* Sleep is not a very robust way to serialize threads */

for(i=0; i<5; ++i) { printf("Wake up a worker, work to do...\n");

rc = pthread_mutex_lock(&mutex); checkResults("pthread_mutex_lock()\n", rc);

/* In the real world, all the threads might be busy, and */ /* we would add work to a queue instead of simply using a flag */ /* In that case the boolean predicate might be some boolean */ /* statement like: if (the-queue-contains-work) */ if (workToDo) {

printf("Work already present, likely threads are busy\n"); } workToDo = 1; rc = pthread_cond_signal(&cond); checkResults("pthread_cond_broadcast()\n", rc);

rc = pthread_mutex_unlock(&mutex); checkResults("pthread_mutex_unlock()\n", rc); sleep(5); /* Sleep is not a very robust way to serialize threads */

}

printf("Main completed\n"); exit(0); return 0;

}

10.4 pthread_cond_timedwait 语法:

#include #include int pthread_cond_timedwait(pthread_cond_t *cond,

pthread_mutex_t *mutex, const struct timespec *abstime);

功能:与 phtread_cond_wait类似,但增加了超时设置,即超过指定时间后,该函数会返回一个 ETIMEOUT的错误信息。

参数:其它略。 Abstime是一个独立的系统时间,注意设置方式。

返回: 0 –成功; ETIMEOUT – 超进退出;其它 – 失败例子:

这个例子中,设置了超时等待的时间为 15秒,注意对时间的处理。 #define _MULTI_THREADED #include #include #include #include #define checkResults(string, val) { \

if(val){ \ printf("Failed with %d at %s", val, string); \ exit(1); \

}\ }

/* For safe condition variable usage, must use a boolean predicate and */ /* a mutex with the condition. */ int workToDo = 0; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

#define NTHREADS 3 #define WAIT_TIME_SECONDS 15

void *threadfunc(void *parm)

{ int rc; struct timespec ts; struct timeval tp;

rc = pthread_mutex_lock(&mutex); checkResults("pthread_mutex_lock()\n", rc);

/* Usually worker threads will loop on these operations */

while (1) { rc = gettimeofday(&tp, NULL); checkResults("gettimeofday()\n", rc);

/* Convert from timeval to timespec */ ts.tv_sec = tp.tv_sec; ts.tv_nsec = tp.tv_usec * 1000;

ts.tv_sec += WAIT_TIME_SECONDS;

while (!workToDo) { printf("Thread blocked\n"); rc = pthread_cond_timedwait(&cond, &mutex, &ts); /* If the wait timed out, in this example, the work is complete, and */ /* the thread will end. */ /* In reality, a timeout must be accompanied by some sort of checking */ /* to see if the work is REALLY all complete. In the simple example */ /* we will just go belly up when we time out. */ if (rc == ETIMEDOUT) {

printf("Wait timed out!\n"); rc = pthread_mutex_unlock(&mutex); checkResults("pthread_mutex_lock()\n", rc); pthread_exit(NULL);

} checkResults("pthread_cond_timedwait()\n", rc); }

printf("Thread consumes work here\n"); workToDo = 0; }

rc = pthread_mutex_unlock(&mutex); checkResults("pthread_mutex_lock()\n", rc); return NULL;

}

int main(int argc, char **argv)

{ int rc=0; int i; pthread_t threadid;

printf("Enter Testcase - %s\n", argv);

printf("Create %d threads\n", NTHREADS);

for(i=0; i<NTHREADS; ++i) { rc = pthread_create(&threadid, NULL, threadfunc, NULL); checkResults("pthread_create()\n", rc);

}

rc = pthread_mutex_lock(&mutex); checkResults("pthread_mutex_lock()\n", rc);

printf("One work item to give to a thread\n"); workToDo = 1; rc = pthread_cond_signal(&cond); checkResults("pthread_cond_signal()\n", rc);

rc = pthread_mutex_unlock(&mutex); checkResults("pthread_mutex_unlock()\n", rc);

printf("Wait for threads and cleanup\n");

for (i=0; i<NTHREADS; ++i) { rc = pthread_join(threadid, NULL); checkResults("pthread_join()\n", rc);

}

pthread_cond_destroy(&cond); pthread_mutex_destroy(&mutex); printf("Main completed\n"); return 0;

}

10.5 pthread_cond_broadcast 与 pthread_cond_signal函数类似,不同点仅在于 pthread_cond_broadcast是用来唤起所有根据该条件变量挂起的线程。而 pthread_cond_signal只能唤起至少一个(多数时候就只是一个),达不到所有的效果。

其它略。

10.6 pthread_cond_destroy 用来销毁一个已分配资源的条件变量。如果该条件变量在使用中(即有其它线程通过该条件变量挂起进),系统将会返回一个 EBUSY的错误信息。其它略。

10.7 pthread_condattr_destroy 10.8 pthread_condattr_getpshared 10.9 pthread_condattr_init 10.10 pthread_condattr_setpshared 10.11pthread_get_expiration_np 11读/写锁的同步 API Read/write lock synchronization API

11.1 pthread_rwlock_destroy 11.2 pthread_rwlock_init 11.3 pthread_rwlock_rdlock 11.4 pthread_rwlock_timedrdlock_np 11.5 pthread_rwlock_timedwrlock_np 11.6 pthread_rwlock_tryrdlock 11.7 pthread_rwlock_trywrlock 11.8 pthread_rwlock_unlock 11.9 pthread_rwlock_wrlock

11.10pthread_rwlockattr_destroy 11.11 pthread_rwlockattr_getpshared 11.12pthread_rwlockattr_init 11.13 pthread_rwlockattr_setpshared 12其它 API --Signal APIs

12.1 pthread_kill 12.2 pthread_sigmask 12.3 pthread_signal_to_cancel_np 13互斥体 API

13.1 互斥体操作 API --Mutex Operation API 互斥体是最简单的一种实现对共享资源保护的方法。当一个线程成功 lock了互斥体之后,该线程就成为这个互斥体的拥有者;当其它的线程试图对互斥体执行 lock操作时,将不会成功,直到互斥体的拥有者对互斥体执行 unlock操作。

也就是说互斥体并不作用于任何变量,它仅仅用来控制是否阻塞线程。

每个创建的互斥体最后都必须使用 pthread_mutex_destory函数来销毁它。系统会检查互斥体是否被销毁。大量的互斥体使用完毕而未销毁,将会影响系统性能。所以在释放、回收利用互斥体的存储空间之前,一定要使用 phthread_mutex_destory函数进行销毁。

当一个互斥体创建之后,就不能再 COPY和或 MOVE到新地址中,否则将不能再有效使用。

下表列出互斥体的主要属性,以此这些属性的默认值,可用的值。

Attribute Default value Supported values

pshared PTHREAD_PROCESS_PRIVATE PTHREAD_PROCESS_PRIVATE or PTHREAD_PROCESS_SHARED kind (non portable) PTHREAD_MUTEX_NONRECURSIV E_NP PTHREAD_MUTEX_NONRECURSIVE_NP or PTHREAD_MUTEX_RECURSIVE_NP name (non portable) PTHREAD_DEFAULT_MUTEX_NAM E_NP "QP0WMTX UNNAMED" Any name that is 15 characters or less. If not terminated by a null character, name is truncated to 15 characters. type PTHREAD_MUTEX_DEFAULT (PTHREAD_MUTEX_NORMAL) PTHREAD_MUTEX_DEFAULT or PTHREAD_MUTEX_NORMAL or PTHREAD_MUTEX_RECURSIVE or PTHREAD_MUTEX_ERRORCHECK or PTHREAD_MUTEX_OWNERTERM_NP The PTHREAD_MUTEX_OWNERTERM_NP attribute value is non portable.

13.1.1 pthread_lock_global_np 语法:

#include int pthread_lock_global_np(void);

功能:这个函数将会锁住一个在线程运行时,由系统提供的全局互斥体。这是个递归的互斥体,名字为“QP0W_GLOBAL_MTX”。

递归时,最大的锁次数为 32767,超过这个数量时,将会返回 ERECURSE的错误码。参数:



返回: 0 – 成功非 0 --失败

例子:

注意下面例子的使用,对于这个系统创建的互斥体,我们不需要对其进行定义。同时互斥体的调用是可以递归的,注意到在子线程中,先对互斥体进行了 lock操作,然后子线程调用的函数中再次对互斥体进行了 lock操作。

这种可以递归的互斥体,在同一线程中的 lock与 unlock操作一定要匹配。同时,递归仅限于当前线程之内。

在这个例子中,如果调用时不带参数,那么各个子线程实际将会执行串行操作,最后得到正确的结果。

Give any number of parameters to show data corruption Creating 10 threads Wait for results

Using 10 threads and LOOPCONSTANT = 5000 Values are: (should be 50000) ==>50000, 50000, 50000, 50000 Main completed

如果调用时带了参数(随便什么都可以),那么各个子线程将会实现并发操作,最后得到一个预期以外的结果(结果应该是随机的,但一定小于正确的结果):

Give any number of parameters to show data corruption A parameter was specified, no serialization is being done! Creating 10 threads Wait for results Using 10 threads and LOOPCONSTANT = 5000 Values are: (should be 50000)

==>34785, 37629, 48219, 47632 Main completed

#include #include #define checkResults(string, val) { \

if(val){ \ printf("Failed with %d at %s", val, string); \ exit(1); \

}\ } /*

This example shows the corruption that can result if no serialization is done and also shows the use of pthread_lock_global_np(). Call this test with no parameters to use pthread_lock_gloabl_np() to protect the critical data, between more than one (possibly unrelated) functions. Use 1 or more parameters to skip locking and show data corruption that occurs without locking.

*/ #define LOOPCONSTANT 5000 #define THREADS 10

int i,j,k,l; int uselock=1;

void secondFunction(void)

{ int rc; if (uselock) {

rc = pthread_lock_global_np(); checkResults("pthread_lock_global_np()\n", rc);

} --i; --j; --k; --l; if (uselock) {

rc = pthread_unlock_global_np(); checkResults("pthread_unlock_global_np()\n", rc); } }

void *threadfunc(void *parm)

{ int loop = 0; int rc;

for (loop=0; loop<LOOPCONSTANT; ++loop) {

if (uselock) { rc = pthread_lock_global_np(); checkResults("pthread_lock_global_np()\n", rc);

} ++i; ++j; ++k; ++l; secondFunction(); ++i; ++j; ++k; ++l; if (uselock) {

rc = pthread_unlock_global_np(); checkResults("pthread_unlock_global_np()\n", rc);

} } return NULL;

}

int main(int argc, char **argv)

{ pthread_t threadid; int rc=0; int loop=0;

printf("Enter Testcase - %s\n", argv); printf("Give any number of parameters to show data corruption\n"); if (argc != 1) {

printf("A parameter was specified, no serialization is being done!\n"); uselock = 0; }

if (uselock) { rc = pthread_lock_global_np(); checkResults("pthread_lock_global_np() (main)\n", rc);

}

printf("Creating %d threads\n", THREADS);

for (loop=0; loop<THREADS; ++loop) { rc = pthread_create(&threadid, NULL, threadfunc, NULL); checkResults("pthread_create()\n", rc);

}

sleep(5);

if (uselock) { rc = pthread_unlock_global_np(); checkResults("pthread_unlock_global_np() (main)\n", rc);

}

printf("Wait for results\n");

for (loop=0; loop%d, %d, %d, %d\n", i, j, k, l);

printf("Main completed\n"); return 0; }

鸟人 发表于 2010-7-31 19:36:13

13.1.2 pthread_unlock_global_np 与 pthread_lock_globak_np的使用方法类似,只不过是用于对全局互斥体的解锁操作,略。

13.1.3 pthread_mutex_init 互斥体初始化语法:

#include int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);



pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

功能:对互斥体进行初始化。如果 attr参数调为 NULL,互斥体属性将会设置成为默认参数。当已具备如下定义后:

pthread_mutex_t mutex2; pthread_mutex_t mutex3; pthread_mutexattr_t mta;

pthread_mutexattr_init(&mta);

下面这三种对互斥体进行初始化的方法是等价的,它们都将互斥体初始化为默认属性。

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_init(&mutex2, NULL); pthread_mutex_init(&mutex3, &mta); 但是使用 PTHREAD_MUTEX_INITIALIZER这种方式对互斥体进行初始化,将不会立

刻生效,而是在其后首次使用 pthread_mutex_lock或 pthread_mutex_trylock函数时才会进行互斥体初始化。这是因为一个互斥体并不仅仅只是一个存储目标,它还需要系统分配相应的资源。于是,用这种方法对互斥体进行初始化,如果在互斥体没有被锁住时就执行 pthread_mutex_destroy,或 pthread_mutex_unlock操作,系统将会报一个 EINVAL的错误信息。

参数:

mutex

一个互斥体目标的地址。

Attr 标识互斥体属性目标的地址。(即互斥体属性结构体的地址位),可以为 NULL

返回: 0 --成功非 0 --失败

例子:略

13.1.4 pthread_mutex_lock 互斥体锁语法:

#include int pthread_mutex_lock(pthread_mutex_t *mutex)

功能:

当一个线程对互斥体成功执行了 pthread_mutex_lock操作后,其它的线程再对这个互斥体执行 lock操作时,将会被阻塞,直到当前线程对互斥体进行 unlock操作。然后就会有另一个等待执行 lock操作的线程再对互斥体进行 lock操作。参数:

mutex

要锁住的互斥体的地址。返回: 0 成功;非 0 失败

例子:下面这个例子中,通过使用互斥体,实现各个子线程实际上的串行。当没有调用参数时,程序会使用互斥体,来实际各个子线程实际上的串行,达到对关键

数据保护的目的,最后将会计算得出预期中正确的结果。如果有调用参数(随便什么参数均可),则程序将不会使用互斥体,子线程之间并行,对关键数据没有保护,此时将会计算出一个非预期的随机的错误结果。

#include #include #define checkResults(string, val) { \

if(val){ \ printf("Failed with %d at %s", val, string); \

exit(1); \ } \ } #define LOOPCONSTANT 10000 #define THREADS 10

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int i,j,k,l; int uselock=1;

void *threadfunc(void *parm)

{ int loop = 0; int rc;

for (loop=0; loop<LOOPCONSTANT; ++loop) {

if (uselock) { rc = pthread_mutex_lock(&mutex); checkResults("pthread_mutex_lock()\n", rc);

} ++i; ++j; ++k; ++l; if (uselock) {

rc = pthread_mutex_unlock(&mutex); checkResults("pthread_mutex_unlock()\n", rc);

} } return NULL;

}

int main(int argc, char **argv) { pthread_t threadid;

int rc=0; int loop=0; pthread_attr_t pta;

printf("Entering testcase\n"); printf("Give any number of parameters to show data corruption\n"); if (argc != 1) {

printf("A parameter was specified, no serialization is being done!\n"); uselock = 0; }

pthread_attr_init(&pta); pthread_attr_setdetachstate(&pta, PTHREAD_CREATE_JOINABLE);

printf("Creating %d threads\n", THREADS);

for (loop=0; loop<THREADS; ++loop) { rc = pthread_create(&threadid, &pta, threadfunc, NULL); checkResults("pthread_create()\n", rc);

}

printf("Wait for results\n");

for (loop=0; loop%d, %d, %d, %d\n", i, j, k, l);

printf("Main completed\n"); return 0; }

13.1.5 pthread_mutex_unlock 互斥体解锁与 pthread_mutex_lock类似,只不过是解锁指令,略

13.1.6 pthread_mutex_destroy 销毁互斥体语法:

#include int pthread_mutex_destroy(pthread_mutex_t *mutex)

功能:销毁指定的互斥体,销毁后的互斥体将不能再使用。互斥体只能由它的拥有者来销毁,如果别的线程锁住互斥体,当前线程执行

pthread_mutex_destroy函数时,将会得到一个 EBUSY的错误信息。

如果销毁互斥体时,其它的通过调用 pthread_mutex_lock函数,正处于阻塞状态中的线程将会由系统返回信息,并得到一个 EDESTROYED的错误信息。参数:

mutex

互斥体结构的地址。返回: 0 –正常;非 0 --失败例子:见 pthread_mutex_lock中的例子,略。

13.1.7 pthread_mutex_timedlock_np (带超时设置的互斥体锁)

语法:

#include #include int pthread_mutex_timedlock_np(pthread_mutex_t *mutex, const struct timespec *deltatime);

功能:

带超时设置的互斥体的 lock操作。即如果当前互斥体已被别的线程锁住,那么当前线程会阻塞等待,超过指定的时间之后,将会返回一个 EBUSY的返回信息码。参数:

mutex

指定的互斥体

deltatime

指定的超时时长返回: 0 --成功; EBUSY(3029) – 超时退出;其它值 – 其它错误;例子:这个例子中,主线程一直锁住了互斥体,如果直接使用 pthread_mutex_lock操作的话,子线程将会与直等待,而主线程又等待子线程的结束,结果将会造成死锁。所以说,合理使用 pthread_mutex_timedlock_np,以及 pthread_mutex_trylock,可以有效的避免死锁。

#define _MULTI_THREADED

#include

#include

#include

#define checkResults(string, val) { \ if(val){ \ printf("Failed with %d at %s", val, string); \ exit(1); \ }\

}

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void *threadFunc(void *parm)

{ int rc; int i; struct timespec deltatime;

deltatime.tv_sec = 5;

deltatime.tv_nsec = 0;

printf("Timed lock the mutex from a secondary thread\n"); rc = pthread_mutex_timedlock_np(&mutex, &deltatime); if (rc != EBUSY) {

printf("Got an incorrect return code from pthread_mutex_timedlock_np\n"); } printf("Thread mutex timeout\n"); return 0;

}

int main(int argc, char **argv)

{ int rc=0; pthread_t thread;

printf("Enter Testcase - %s\n", argv);

printf("Acquire the mutex in the initial thread\n"); rc = pthread_mutex_lock(&mutex); checkResults("pthread_mutex_lock()\n", rc);

printf("Create a thread\n"); rc = pthread_create(&thread, NULL, threadFunc, NULL); checkResults("pthread_create()\n", rc);

printf("Join to the thread\n"); rc = pthread_join(thread, NULL); checkResults("pthread_join()\n", rc);

printf("Destroy mutex\n"); pthread_mutex_destroy(&mutex);

printf("Main completed\n"); return 0; }

13.1.8 pthread_mutex_trylock (不进行阻塞处理的互斥体锁)

语法:

#include int pthread_mutex_trylock (pthread_mutex_t *mutex)

功能:这个函数试图锁住一个互斥体,但并不进行阻塞处理。也就是如果执行时,发现该互斥体已被别的线程锁住时,函数将不会阻塞,而是返回一个 EBUSY的错误信息。如果当前线程已锁住这个互斥体时,函数将会返回一个 EDEADLK的错误信息。

参数、返回与函数 pthread_mutex_lock相同,略。例子:这个例子比较具有实用,因为通过 pthread_mutex_lock来控制保护关键数据,造成了对关键数据的操作实质上是处于串行中。

此处通过非阻塞的 pthread_mutex_trylock,充分利用了各个线程自已的资源去进行计算,同时尽可能的实时更改关键数据。最后,计算出各个线程本地计算量的百分比。得出的结果显示大部分计算量都是由各个线程自已完成,程序效率大大提高。

最后输出的参考结果:

Creating 10 threads Wait for results Thread processed about 95% of the problem locally Thread processed about 93% of the problem locally Thread processed about 88% of the problem locally Thread processed about 85% of the problem locally Thread processed about 98% of the problem locally Thread processed about 95% of the problem locally Thread processed about 94% of the problem locally Thread processed about 91% of the problem locally Thread processed about 81% of the problem locally Thread processed about 60% of the problem locally

Cleanup and show results Using 10 threads and LOOPCONSTANT = 100000 Values are: (should be 1000000)

==>1000000, 1000000, 1000000, 1000000 Main completed

#include #include #include #define checkResults(string, val) { \

if(val){ \ printf("Failed with %d at %s", val, string); \ exit(1); \

}\ }

/* This example simulates a number of threads working on a parallel problem. The threads use pthread_mutex_trylock() so that they do not spend time blocking on a mutex and instead spend more of the time making progress towards the final solution. When trylock fails, the processing is done locally, eventually to be merged with the final parallel solution.

This example should complete faster than the example for pthread_mutex_lock() in which threads solve the same parallel problem but spend more time waiting in resource contention. */

#define LOOPCONSTANT 10000 #define THREADS 10

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int i,j,k,l;

void *threadfunc(void *parm)

{ int loop = 0; int localProcessingCompleted = 0; int numberOfLocalProcessingBursts = 0; int processingCompletedThisBurst = 0; int rc;

for (loop=0; loop<LOOPCONSTANT; ++loop) { rc = pthread_mutex_trylock(&mutex); if (rc == EBUSY) {

/* Process continue processing the part of the problem */ /* that we can without the lock. We do not want to waste */ /* time blocking. Instead, we'll count locally. */ ++localProcessingCompleted; ++numberOfLocalProcessingBursts; continue;

} /* We acquired the lock, so this part of the can be global*/ checkResults("pthread_mutex_trylock()\n", rc); /* Processing completed consist of last local processing */ /* plus the 1 unit of processing this time through */ processingCompletedThisBurst = 1 + localProcessingCompleted; localProcessingCompleted = 0; i+=processingCompletedThisBurst; j+=processingCompletedThisBurst; k+=processingCompletedThisBurst; l+=processingCompletedThisBurst;

rc = pthread_mutex_unlock(&mutex);

checkResults("pthread_mutex_unlock()\n", rc); } /* If any local processing remains, merge it with the global*/ /* problem so our part of the solution is accounted for */ if (localProcessingCompleted) {

rc = pthread_mutex_lock(&mutex); checkResults("final pthread_mutex_lock()\n", rc);

i+=localProcessingCompleted; j+=localProcessingCompleted; k+=localProcessingCompleted; l+=localProcessingCompleted;

rc = pthread_mutex_unlock(&mutex);

checkResults("final pthread_mutex_unlock()\n", rc); } printf("Thread processed about %d%% of the problem locally\n",

(numberOfLocalProcessingBursts * 100) / LOOPCONSTANT); return NULL; }

int main(int argc, char **argv)

{ pthread_t threadid; int rc=0; int loop=0; pthread_attr_t pta;

printf("Entering testcase\n");

pthread_attr_init(&pta); pthread_attr_setdetachstate(&pta, PTHREAD_CREATE_JOINABLE);

printf("Creating %d threads\n", THREADS);

for (loop=0; loop<THREADS; ++loop) { rc = pthread_create(&threadid, &pta, threadfunc, NULL); checkResults("pthread_create()\n", rc);

}

printf("Wait for results\n");

for (loop=0; loop%d, %d, %d, %d\n", i, j, k, l);

printf("Main completed\n"); return 0; }

13.2 互斥体属性设置 13.2.1 pthread_mutexattr_init 互斥体属性初始化 13.2.2 pthread_mutexattr_destroy 销毁互斥体属性

Professional Group Tec. Doc.07121901 Author-万一飞

13.2.3 pthread_mutexattr_setkind_np 设置互斥体种类属性 13.2.4 pthread_mutexattr_setname_np 设置互斥体属性名字 13.2.5 pthread_mutexattr_setpshared 设置互斥体中进程属性 13.2.6 pthread_mutexattr_settype 设置互斥体类型属性 13.2.7 pthread_mutexattr_default_np 设置互斥体为默认属性 13.3 取互斥体属性 API – Mutex Attribute API 13.3.1 pthread_mutexattr_getkind_np 取互斥体种类 13.3.2 pthread_mutexattr_getname_np 取互斥体属性目标名 13.3.3 pthread_mutexattr_getpshared 从互斥体中取出进程共享的属性 13.3.4 pthread_mutexattr_gettype 取互斥体类型

14信号量的 API

与信号量相关的 API,其实操作的都是一个信号量组 (semaphore set),或者是一套信号量,总之就是多个信号量的集合。一个信号量组里面,会有多个信号量。

14.1 semget – 带 KEY值取信号量描述符语法:

#include #include

int semget(key_t key, int nsems, int semflg);

功能:

semget这个函数既可以创建一个新的信号量组并返回其描述符,也可以通过 KEY值返回一个已存在的信号量组的描述符。当满足以下任一一个条件时,该函数将会创建新的信号量组:

5、 key参数为 IPC_PRIVATE 6、根据 key参数没有找到已在的信号量组,同时 semflg参数为 IPC_CREAT(此时生成的信号量组即带 key参数)

该函数不会更改已存在的信号量组的状态。当创建新信号量组时,系统将会对 semid_ds结构体中的成员进行如下的初始化赋值: sem_perm.cuid, sem_perm.uid将会赋值为当前线程的用户 ID; sem_perm.guid, sem_perm.gid将会赋值为当前线程的 group ID; sem_perm.mode的低 9位,赋值成为输入参数 semflg的低 9位。

sem_nsems 赋值成为输入参数 nsems sem_ctime赋值成为当前时间 sem_otime赋值为 zero 参数: KEY

输入参数,根据此 KEY查找,或生成信号量组。这个参数为 IPC_PRIVATE(0x00000000)时,此时会生成一个信号量组。用户可以自行指定这个参数,或由函数 ftok()来产生

nsems

输入参数,应该是仅在生成信号量时使用。表明该信号量组中,信号量的数量。当信号量生成之后,这个值不能再更改。如果访问的是一个已存在的信号量,这个值可以赋为 zero..

Semflg 输入参数,操作权限的标识。可以赋值为 zero,或是以下的值: S_IRUSR

允许该信号量的拥有者从中读取数据

S_IWUSR

允许该信号量的拥有者向其中写数据

S_IRGRP

允许该信号量的用户组从中读取数据

S_IWGRP

允许该信号量的用户组向其中写数据

S_IROTH

允许其它用户读取该组信号量

S_IWOTH

允许其它用户向该信号量中写 IPC_CREAT

如果相应 KEY值的信号量不存在,那么建立新的信号量 IPC_EXCL

如果设置了 IPC_CREAT,而信号量已存在时,返回错误

返回: -1 函数发生错误其它值该信号量的唯一标识符

例子:如下面这个不完整的例子中,定义了一个整型变量 semaphoreID,做为信号量的描述符,要赋初始值为-1,即与函数的错误返回相同,以便错误判断处理。这里就创建了一个信号量组,该组中只有一个信号量。

#include #include int semaphoreID = -1 ; semaphoreId = semget(IPC_PRIVATE, 1, S_IRUSR|S_IWUSR);

14.2 semop 对信号量组进行操作语法:

#include

int semop(int semid, struct sembuf *sops, size_t nsops);

功能:对已存在的信号量组执行操作。根据输入的参数,该命令可以对一组信号量中的多个信号量执行操作,也可以对同一信

号量执行多次操作。(可能后者的用法会比较少吧,虽然理论上是可行的)参数:

semid

信号量组的描述符

sops 一个数组的指针,该数组是由一个 sembuf结构的结构体组成。这个结构体用标明

用来具体的操作内容项。

sembuf结构体的定义如下:

struct sembuf { /* sops即是 semaphore operation structure 的简称*/

unsigned short sem_num; /* 信号量组中,要操作的信号量的编号(位置) */

short sem_op; /* 操作数值 */

short sem_flg; /*操作标志 SEM_UNDO and IPC_NOWAIT */

}

该数组中,一条记录(即一个结构体),就标识了一次操作。如果有多条记录(多个结构体),那么可以执行多次操作。结构体中的 sem_num变量标识的是信号量组中,指定的信号量的编号,由 0开始。即该组信号量中,第一个信号量的编号为 0,第二个信号量为 1。

Nsops

上述数组中的记录个数(即结构体的个数),也就是要执行的操作次数。返回:

0操作成功

非 0 操作失败注意事项:

系统根据入口参数中的 sops,逐笔对信号量执行操作直至完毕。当执行 sem_op操作时,其它的线程都不能再操作这个信号量组,直到当前线程结束操作,或被挂起。

如果结构体中的 sem_op参数是正数,函数将会增加指定的信号量的数值,然后唤起相应数量的?重新运行条件为信号量增加而的线程,这也就相当于通过信号量释放资源。如果结构体中的 sem_op参数是负数,函数将会减去指定的信号量的数值。当结果为负数的时候,线程将会挂起,直到该信号量增加;(即当前线程重新运行

条件为指定的信号量增加)

如果结果是正数,就仅仅只是减去信号量的数值而已;

如果结果是 0,将会唤起相应数量的?重新运行条件为信号量数值等于 0的线程。

如果结构体中的 sem_op的参数为 0,就表示挂起当前线程,直到指定的信号量数值为

0. 如果 sem_flg参数设置为 IPC_NOWAIT,那么当前线程不能运行时,将不会被挂起,而是直接返回 EAGAIN的错误码。

如果 sem_flg参数设置为 SEM_UNDO,那么当线程结束时,对指定信号量的操作将会回滚,也就是通过信号量来控制资源的回收与申请。

如果 sem_flg参数设置为 0,就表示只进行标准处理,没有其它特殊操作。

当使用 sem_op操作挂起线程的时候,这个线程可以被其它的线程中断。

例子:

在下面这个例子里,先生成了一个信号量组,这个组里只有一个信号量。然后对这个信号量进行操作。

LockOperation就是一个 sembuf结构的变量,

第一位 0.,表示操作信号量组中的第一个信号量;

第二位 -1,表示对信号量进行减操作;

第三位 0,表示没有特殊处理

在执行 semop函数时,首位参数 semaporeId,表示信号量组的描述符;第二位参数传递的是 lockOperation地址第三位参数 1,表示第二位参数传递的内容中,只含一次操作的内容。

#include #include int rc; int semaphoreID = -1 ; struct sembuf lockOperation = { 0, -1, 0}; semaphoreId = semget(IPC_PRIVATE, 1, S_IRUSR|S_IWUSR); rc = semop(semaphoreId, &lockOperation, 1);

14.3 semctl --对信号量进行控制操作语法:

#include int semctl(int semid, int semnum, int cmd, 可选参数);

功能:

这个函数允许调用者控制指定的信号量。

调用者通过 cmd参数,来表达需要进行控制的内容:

IPC_RMID(0x00000000)从系统中移除信号量组描述符,销毁信号量组。所有执行 sem_op操作后,基于这个信号量组而挂起的线程,将会返回一个-1,同时错误码为 EIDRM

IPC_SET(0x00000001)通过第 4个可选参数,设置 semid_ds数据结构中的 sem_perm.uid,sem_perm.gid, sem_perm.mode的值。这里,第 4个参数此时是一个指向 semid_ds类型结构体的指针。

IPC_STAT(0x00000002)通过第 4个可选参数,保存 semid_ds数据结构中的当前值。第 4个参数此时是一个指向 semid_ds类型结构体的指针。

GETNCNT(0x00000003)返回唤起条件为“指定的信号量数值增加”的线程数量。这个数值对应 semaphore_t 结构中的 semncnt。

GETPID (0x00000004) 返回最后一个操作指定信号量的线程所属的进程 ID。这个数值对应 semaphore_t 结构中的 sempid.

GETVAL(0x00000005) 返回指定的信号量的当前数值。这个数值对应 semaphore_t结构中的 semval.

GETALL(0x00000006)通过第四个可选参数,返回这个信号量组中,所有信号量当前的值。这个参数此时是一个数组指针,数组中元素类型为 unsigned short.

GETZCNT(0x00000007)返回唤起条件为“指定的信号量数值等于 0”的线程数量。这个数值对应 smeaphore_t 结构中的 semzcnt.

SETVAL(0x00000008)

将指定的信号量的数值赋值为第 4个可选参数,此时该参数类型需要为 int.同时清除与信号量有关联的每个线程的信号量调整值(Set the value of semaphore semnum to the integer value of type int specified in the fourth parameter and clear the associated per-thread semaphore adjustment value.)

SETALL(0x00000009)

通过第 4个可选参数,将信号量组中的每一个信号量的值都进行相应更改。此时第 4个参数是一个数组指针,数组中元素类型为 unsigned short.同时,与每个信号量有关联的线程,都会清除其信号量调整值。( In addition, the associated per-thread semaphore-adjustment value is cleared for each semaphore)

参数:

semid

输入参数,正整数,信号量组描述符。

Semnum 输入参数,非负整数。标识指定信号量在信号量组中的编号。编号从 0开始,即该组信号量中,第一个信号量的编号为 0,第二个信号量的编号为 1.

Cmd

控制符,见上文中的描述。

可选参数根据具体情况而定,有时表示输入,有时表示输出。有时是一个整型变量,有时是一个数组指针,详情见上文中的描述。返回:当返回成功时,视 cmd参数的不同,会有不同的返回值

GETVAL返回信号量的数值 GETPID 返回进程号 GETNCNT返回符合条件的线程数量 GETZCNT 返回符合条件的线程数量其它操作返回 0

当函数失败时,返回-1

例子:下面这个简单的例子中,接上面 semop操作的内容,对信号量组进行了 SETVAL的控制操作,将这个信号量组中的第一个信号量的数值设置为 1.

#include #include int rc; int semaphoreID = -1 ; struct sembuf lockOperation = { 0, -1, 0}; semaphoreId = semget(IPC_PRIVATE, 1, S_IRUSR|S_IWUSR); rc = semop(semaphoreId, &lockOperation, 1);

rc = semctl(semaphoreId, 0, SETVAL, (int)1);

15其它

15.1 spawn 语法: #include pid_t spawn( const char *path, const int fd_count, const int fd_map[], const struct inheritance *inherit, char * const argv[], char * const envp[]); 功能:

参数:

path

输入参数,可执行文件名。

Fd_count

输入参数,子进程可以继承的文件描述符

fd_map[]

输入参数,子进程接收到当前进程传递给它的文件描述符数组

inherit 输入参数,一个 inheritance类型的结构的地址。返回:

例子:

下面这个例子演示了通过 spawn函数,创建一个进程,新的进程继承了当前进程的 socket 描述符。 /**************************************************************************/ /* Application creates an child process using spawn(). */ /**************************************************************************/

#include #include #include #include #include

#define SERVER_PORT 12345

main (int argc, char *argv[])

{ int i, num, pid, rc, on = 1; int listen_sd, accept_sd; int spawn_fdmap; char *spawn_argv; char *spawn_envp;

struct inheritance inherit; struct sockaddr_in addr;

/*************************************************/ /* If an argument was specified, use it to */ /* control the number of incoming connections */ /*************************************************/ if (argc >= 2)

num = atoi(argv); else num= 1;

/*************************************************/ /* Create an AF_INET stream socket to receive */ /* incoming connections on */ /*************************************************/ listen_sd = socket(AF_INET, SOCK_STREAM, 0); if (listen_sd < 0) {

perror("socket() failed"); exit(-1); }

/*************************************************/ /* Allow socket descriptor to be reuseable */ /*************************************************/ rc = setsockopt(listen_sd,

SOL_SOCKET, SO_REUSEADDR,

(char *)&on, sizeof(on)); if (rc < 0) {

perror("setsockopt() failed"); close(listen_sd); exit(-1);

}

/*************************************************/ /* Bind the socket */ /*************************************************/ memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(SERVER_PORT); addr.sin_addr.s_addr = htonl(INADDR_ANY); rc = bind(listen_sd,

(struct sockaddr *)&addr, sizeof(addr)); if (rc < 0) {

perror("bind() failed"); close(listen_sd); exit(-1);

}

/*************************************************/ /* Set the listen back log */ /*************************************************/ rc = listen(listen_sd, 5); if (rc < 0) {

perror("listen() failed"); close(listen_sd); exit(-1);

}

/*************************************************/ /* Inform the user that the server is ready */ /*************************************************/ printf("The server is ready\n");

/*************************************************/ /* Go through the loop once for each connection */ /*************************************************/ for (i=0; i < num; i++) {

/**********************************************/ /* Wait for an incoming connection */ /**********************************************/ printf("Interation: %d\n", i+1); printf(" waiting on accept()\n"); accept_sd = accept(listen_sd, NULL, NULL); if (accept_sd < 0) {

perror("accept() failed"); close(listen_sd); exit(-1);

} printf(" accept completed successfully\n");

/**********************************************/ /* Initialize the spawn parameters */ /* */

/* The socket descriptor for the new */ /* connection is mapped over to descriptor 0 */ /* in the child program. */ /**********************************************/ memset(&inherit, 0, sizeof(inherit)); spawn_argv = NULL; spawn_envp = NULL; spawn_fdmap = accept_sd;

/**********************************************/ /* Create the worker job */ /**********************************************/ printf(" creating worker job\n"); pid = spawn("/QSYS.LIB/QGPL.LIB/WRKR1.PGM",

1, spawn_fdmap, &inherit,

spawn_argv, spawn_envp); if (pid < 0) {

perror("spawn() failed"); close(listen_sd); close(accept_sd);

exit(-1); } printf(" spawn completed successfully\n");

/**********************************************/ /* Close down the incoming connection since */ /* it has been given to a worker to handle */ /**********************************************/ close(accept_sd);

}

/*************************************************/ /* Close down the listen socket */ /*************************************************/ close(listen_sd);

}

51ak 发表于 2010-7-31 23:53:54

没有看懂啊。

netegg 发表于 2010-8-1 06:35:30

本帖最后由 netegg 于 2010-8-1 06:37 编辑

哥们你倒是把代码整理下呀,那些注释缩短点,看着也方便点,本来会c的就不多

鸟人 发表于 2010-8-1 07:37:14

我也不懂...

ajian55 发表于 2010-8-1 09:37:24

本帖最后由 ajian55 于 2010-8-1 09:41 编辑

LZ啊 这是au3论坛啊 不是c/c++你弄个c/c++代码来 看懂又有何用? c/c++里 多线程是基本的编程技术 是au3 本身的限制 就算c/c++的多线程再熟悉想用au3来实现 也是做梦~

republican 发表于 2010-8-5 19:16:23

AU3本体会有许多阻塞..如果是非DLL调用的多线程调用也没用,反正主程序就给你塞死了。

曼菲士 发表于 2010-9-27 13:25:59

C语言的想和AU3合用?

wggaijcm 发表于 2011-2-13 07:47:18

 有点意思

leon460 发表于 2011-3-4 13:30:28

好乱啊,,怎不能贴出代码形式?

曼菲士 发表于 2011-3-25 13:59:04

C语言代码,难看。。。

bdancerlc 发表于 2011-11-28 00:22:14

0.0, 这么乱,世外高人看个十年八载的也许能看懂...

shuren88 发表于 2012-2-25 11:52:13

看不懂,不过还是谢了

xkowen 发表于 2012-2-26 13:37:16

看起来好像很强大。。。。。。。

shopp1984 发表于 2012-3-4 00:41:36

看到C++就晕了
页: [1] 2
查看完整版本: 多线程编程技术