您的位置:首页 > 运维架构 > Linux

linux多线程编程

2011-03-02 14:20 190 查看
线程的概念就不赘述了,下面先讲讲线程和进程相比具有那些优点或者缺点:

1.线程能够使一个程序看起来在同一时间干了两件或者多件事,这样的功能有时候是非常有用的。

2.线程适用与一个具有几部分相对独立的工作的进程,这样在一个线程阻塞的时候另一个线程的运行不会被中断。

3.由于多核CPU的广泛应用,将一个进程分解为多个线程可以更有效的利用硬件资源。

4.多线程比多进程需要更少的资源,同时更具实用性。

5.多线程程序的设计需要非常小心,一些变量共享或者同步很可能会发生错误。

6.调试多线程程序将会非常复杂。

创建新的线程

在pthread.h里定义了与线程编程相关的一些函数。函数pthread_create创建一个新的线程,与fork函数创建进程类似。函数的定义如下:

int pthread_create(pthread_t *thread, pthread_attr_t *attr, void*(*start_routine)(void *), void *arg);

第一个参数标识了一个新的线程,第二个参数指定了线程的属性,一般情况下可置为NULL,后两个参数标识了新的线程所要执行的函数的名称以及传入这个函数的参数。
终止一个线程
void pthread_exit(void *retval);
等待一个线程
int pthread_join(pthread_t th, void **thread_return);
相当于进程函数里的wait函数。
下面是多线程使用的一个简单示例,首先程序创建了一个新的线程,并将新线程的结果返回给原始线程。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void *thread_function(void *arg);
char message[] = "Hello World";
int main()
{
int res;
pthread_t a_thread;
void *thread_result;
res = pthread_create(&a_thread, NULL, thread_function, (void *)message);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
printf("Waiting for thread to finish.../n");
res = pthread_join(a_thread, &thread_result);
if (res != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf("Thread joined, it returned %s/n", (char *)thread_result);
printf("Message is now %s/n", message);
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg)
{
printf("thread_function is running. Argument was %s/n", (char *)arg);
sleep(3);
strcpy(message, "Bye!");
pthread_exit("Thank you for the CPU time");
}

在编译之前,首先要确认_REENTRANT已经定义了,少数的系统里可能也需要定义_POSIX_C_SOURCE;其次要确认函数库是不是较新的,否则可能无法编译通过。
信号量
关于信号量的概念可以百度或者谷歌。linux下有两种信号量,一种是POSIX的供线程使用的信号量,一种是针对进程的系统V信号量。
创建信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
该函数初始化一个由sem指向的信号量。pshared指定该信号量的共享选项,如果该值为0,则表明该信号量只能由当前进程使用,若不为0,则可以被多个进程使用。一般在linux下,该值为0。value指定该信号量的初始值。
控制信号量的值
int sem_wait(sem_t * sem);
int sem_post(sem_t * sem);
这两个函数的参数都是一个指向信号量的指针。sem_post函数将信号量的值加1。而sem_wait函数在信号量不为0的情况下将信号量减1。若在信号量为0的时候调用该函数,则将使线程阻塞直到信号量的值不为0。如果有两个线程同时在等待一个信号量,那么当信号量的值非0时,将只有一个线程能够继续运行,而另一个线程将继续等待。
销毁信号量
int sem_destroy(sem_t * sem);
当信号量使用完毕时,调用该函数收回由信号量使用的所有资源。
下面的代码演示了信号量的使用:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
void *thread_function(void *arg);
sem_t bin_sem;
#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int main()
{
int res;
pthread_t a_thread;
void *thread_result;
res = sem_init(&bin_sem, 0, 0);
if (res != 0) {
perror("Semaphore initialization failed");
exit(EXIT_FAILURE);
}
res = pthread_create(&a_thread, NULL, thread_function, NULL);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
printf("Input some text. Enter ‘end’ to finish/n");
while(strncmp("end", work_area, 3) != 0) {
fgets(work_area, WORK_SIZE, stdin);
sem_post(&bin_sem);
}
printf("/nWaiting for thread to finish.../n");
res = pthread_join(a_thread, &thread_result);
if (res != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf("Thread joined/n”);
sem_destroy(&bin_sem);
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg)
{
sem_wait(&bin_sem);
while(strncmp("end", work_area, 3) != 0) {
printf("You input %d characters/n", strlen(work_area) -1);
sem_wait(&bin_sem);
}
pthread_exit(NULL);
}

同步互斥
另一种可以让多线程程序达到同步的方法是使用互斥。当希望只有一个线程能访问某个对象时使用互斥,该方法可以锁住某个对象或对某个对象进行解锁。用来进行互斥的函数与信号量的函数在描述以及功能上有些类似。linux提供的关于互斥的函数主要如下:

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t*mutexattr);
int pthread_mutex_lock(pthread_mutex_t *mutex));
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
下面的代码演示了互斥的使用,当中核心变量在使用互斥锁上之后,只能由一个线程访问。而且一些函数返回值的检查被省略了。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
void *thread_function(void *arg);
pthread_mutex_t work_mutex; /* protects both work_area and time_to_exit */
#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int time_to_exit = 0;
int main()
{
int res;
pthread_t a_thread;
void *thread_result;
res = pthread_mutex_init(&work_mutex, NULL);
if (res != 0) {
perror("Mutex initialization failed");
exit(EXIT_FAILURE);
}
res = pthread_create(&a_thread, NULL, thread_function, NULL);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
pthread_mutex_lock(&work_mutex);
printf("Input some text. Enter ‘end’ to finish/n");
while(!time_to_exit) {
fgets(work_area, WORK_SIZE, stdin);
pthread_mutex_unlock(&work_mutex);
while(1) {
pthread_mutex_lock(&work_mutex);
if (work_area[0] != ‘/0’) {
pthread_mutex_unlock(&work_mutex);
sleep(1);
}
else {
break;
}
}
}
pthread_mutex_unlock(&work_mutex);
printf("/nWaiting for thread to finish.../n");
res = pthread_join(a_thread, &thread_result);
if (res != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf("Thread joined/n");
pthread_mutex_destroy(&work_mutex);
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg)
{
sleep(1);
pthread_mutex_lock(&work_mutex);
while(strncmp("end", work_area, 3) != 0) {
printf("You input %d characters/n", strlen(work_area) -1);
work_area[0] = ‘/0’;
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
while (work_area[0] == ‘/0’ ) {
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
}
}
time_to_exit = 1;
work_area[0] = ‘/0’;
pthread_mutex_unlock(&work_mutex);
pthread_exit(0);
}

线程属性
线程的属性中能够被设定的不多,而在能设定的当中,我们将只讨论那些可能会被用到的,其它的函数可以查阅一些帮助文档。在设定线程属性的函数中最重要的函数是:

int pthread_attr_init(pthread_attr_t *attr);
该函数初始化一个线程属性对象。在调用完该函数之后可以对线程的一些属性进行更改,主要包含以下一些函数:

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param*param);
int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param*param);
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit);
int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit);
int pthread_attr_setscope(pthread_attr_t *attr, int scope);
int pthread_attr_getscope(const pthread_attr_t *attr, int *scope);
int pthread_attr_setstacksize(pthread_attr_t *attr, int scope);
int pthread_attr_getstacksize(const pthread_attr_t *attr, int *scope);
这些函数主要对以下属性进行读写:
1.detachedstate 该属性能够消除线程进行应答的必要,两个可能的值为PTHREAD_CREATE_JOINABLE和PTHREAD_CREATE_DETACHED。默认的值为PTHREAD_CREATE_JOINABLE,此时线程之间允许进行响应,当该属性的值为PTHREAD_CREATE_DETACHED时,函数pthread_join将不能调用。
2.schedpolicy 该属性用来对线程进行定位。有三个选项:SCHED_OTHER,SCHED_RP和SCHED_FIFO。默认情况下的值为SCHED_OTHER。
3.schedparam 该属性与schedpolicy搭配使用。
4.inheritsched 该属性有两个可能的值:PTHREAD_EXPLICIT_SCHED和PTHREAD_INHERIT_SCHED。前者表示该线程的调度完全由自己的属性决定,而后者则表示该线程的调度由创建它的线程的属性决定。
5.scope 只有一个值PTHREAD_SCOPE_SYSTEM,该属性决定如何对线程进行调度。
6.stacksize 该属性决定线程的栈的大小。

下面的代码演示了对线程的属性的更改,程序首先新建了一个属性对象,然后将它赋予给一个新创建的线程。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
void *thread_function(void *arg);
char message[] = "Hello World";
int thread_finished = 0;
int main()
{
int res;
pthread_t a_thread;
pthread_attr_t thread_attr;
res = pthread_attr_init(&thread_attr);
if (res != 0) {
perror("Attribute creation failed");
exit(EXIT_FAILURE);
}
res = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
if (res != 0) {
perror("Setting detached attribute failed");
exit(EXIT_FAILURE);
}
res = pthread_create(&a_thread, &thread_attr,
thread_function, (void *)message);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
(void)pthread_attr_destroy(&thread_attr);
while(!thread_finished) {
printf("Waiting for thread to say it’s finished.../n");
sleep(1);
}
printf("Other thread finished, bye!/n");
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg)
{
printf("thread_function is running. Argument was %s/n", (char *)arg);
sleep(4);
printf("Second thread setting finished flag, and exiting now/n");
thread_finished = 1;
pthread_exit(NULL);
}

可能的运行结果如下:

$ ./thread5
Waiting for thread to say it’s finished...
thread_function is running. Argument was Hello World
Waiting for thread to say it’s finished...
Waiting for thread to say it’s finished...
Waiting for thread to say it’s finished...
Second thread setting finished flag, and exiting now
Other thread finished, bye!

关于线程属性的进一步探讨可参阅有关文献,在此不再赘述。
取消一个线程
有时候可能需要在一个线程里让另一个线程终结,此时可以使用如下函数:
int pthread_cancel(pthread_t thread);
同时一个线程还能够通过函数pthread_setcancelstate设置它的“取消状态”。 其定义如下:
int pthread_setcancelstate(int state, int *oldstate);
有两个值,PTHREAD_CANCEL_ENABLE允许线程接收其它线程的终结要求,PTHREAD_CANCEL_DISABLE则拒绝其它线程的该要求。如果接受了其它线程的终结要求,还有第二级选项影响线程的状态,使用函数pthread_setcanceltype进行设定,其定义如下:
int pthread_setcanceltype(int type, int *oldtype);
如果type的值为PTHREAD_CANCEL_ASYNCHRONOUS,则终结要求将被立即执行。若值为PTHREAD_CANCEL_DEFERRED,则终结要求要等线程的以下函数之一执行才能被执行: pthread_join, pthread_cond_wait,pthread_cond_timedwait, pthread_testcancel, sem_wait, 或者sigwait。
下面是一个多线程的例子:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#define NUM_THREADS 6
void *thread_function(void *arg);
int main()
{
int res;
pthread_t a_thread[NUM_THREADS];
void *thread_result;
int lots_of_threads;
for(lots_of_threads = 0; lots_of_threads < NUM_THREADS; lots_of_threads++)
{
res = pthread_create(&(a_thread[lots_of_threads]),
NULL, thread_function, (void *)&lots_of_threads);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
sleep(1);
}
printf("Waiting for threads to finish.../n");
for(lots_of_threads = NUM_THREADS - 1; lots_of_threads >= 0;lots_of_threads--)
{
res = pthread_join(a_thread[lots_of_threads], &thread_result);
if (res == 0) {
printf("Picked up a thread/n");
}
else {
perror("pthread_join failed");
}
}
printf("All done/n");
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg)
{
int my_number = *(int *)arg;
int rand_num;
printf("thread_function is running. Argument was %d/n", my_number);
rand_num=1+(int)(9.0*rand()/(RAND_MAX+1.0));
sleep(rand_num);
printf("Bye from %d/n", my_number);
pthread_exit(NULL);
}

参考文献:《Begining Linux Programming 4th edition》-------Neil Matthew
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: