注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

学习笔记

正确的方法如同学习书法,开始的时候要临摹,临摹好了然后创造自己的风格。

 
 
 

日志

 
 

[Linux笔记]多线程编程  

2011-10-03 14:41:58|  分类: Linux |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

 多线程编程的优点:

* 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。

 

* 使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行在不同的CPU上。

 

* 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序有利于理解和修改。

       这里要说的线程相关操作都是用户空间的操作。在Linux中,一般pthread线程库是一套通用的线程库,是由POSIX提出的,因此具有很好的移植性。

       在Linux系统中,当调用pthread_create函数来创建线程时,系统首先创建一个新的进程,再在该进程中创建新的线程。新创建的进程和原有进程是共享内存的,因此与调用fork产生的进程有本质的区别。

 

/*

1). 在多线程程序中,一个新的线程通常由一个进程调用pthread_create函数而诞生的。新线程创建之后,通常将这个进程称为主线程。

2). 对于多线程程序来说,一旦在进程内创建了一个线程,那么该进程也就摇身变成了一个线程。此刻这两个线程形成了一个线程组,它们共享“以前”的那个所独享的资源。

3). 主线程和子线程之间是并列关系,不存在类似fork()函数那样的父子进程关系,这也是不将创建的进程称为父进程的原因。

 */

 

//m_pthread.c

#include <pthread.h>

#include <stdio.h>

#include <stdlib.h>

 

void *thread(void *arg)

{

    pthread_t newthid;

    newthid=pthread_self();     //get the current thread's id

    printf("This a new thread and thread ID is %lu\n",newthid);

    sleep(500);

    pthread_exit(0);

}

 

int main()

{

    pthread_t thid;

    printf("Main thread, ID is %lu\n",pthread_self());  //Get the main thread's id

 

    if(pthread_create(&thid,NULL,(void *)thread,NULL)!=0)

    {

        printf("Thread creation failed!\n");

        exit(1);

    }

    printf("My ID is %lu, new thread ID is %lu\n",pthread_self(),thid);

    sleep(1000);

    return 0;

}

 

/*

$ gcc mutex_pthread.c -o mutex_pthread -lpthread

        在终端1中运行

$ ./mutex_pthread

        在终端2中查看

$ ps -eLf

UID           PID  PPID   LWP   C    NLWP STIME TTY TIME  CMD

...

fantity    6618 25848  6618  0    2 21:49 pts/1    00:00:00 ./mutex_pthread

fantity    6618 25848  6619  0    2 21:49 pts/1    00:00:00 ./mutex_pthread

...

 

        信息中三类ID信息:PID,PPID和LWP。LWP是轻量级进程的pid,NLWP为进程组中线程的个数。

 */

 

1. 进程的创建和退出

(1)函数说明

       创建线程实际上就是确定该线程函数的入口点,这里通常使用的函数是pthread_create。在线程创建以后,就开始运行相关的线程函数,在该函数运行完之后,该线程也就退出了,这也是线程退出的一种方法。另一种退出线程的方法是使用函数pthread_exit,这是线程的主动行为。这里要注意的是,在使用线程函数时,不能随意使用exit退出函数进行出错处理,由于exit的作用是使调用进程终止,往往是一个进程包含多个线程,因此,在使用exit之后,该进程中的所有线程都终止了。因此,在线程中就可以使用pthread_exit来代替进程中的exit。

       由于一个进程中的多个线程是共享数据段的,因此通常在线程退出之后,退出线程所占用的资源并不会随着线程的终止而得到释放。正如进程之间可以用wait()系统调用来同步终止并释放资源一样,线程之间也有类似的机制,那就是pthread_join()函数。pthread_join可以用于将当前线程挂起,等待进程的结束。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源就被收回。

 

(2)函数格式

pthread_create函数的语法要点。

所需的头文件             #include <pthread.h>

函数原型                    int pthread_create(pthread_t *thread , pthread_attr_t *attr , void *(*start_routine)(void *) , void *arg)

函数传入值                 thread:线程标识符

                                 attr:线程属性设置

                                 start_routine:线程函数的起始地址

                                 arg:传递给start_routine的参数

函数返回值:              成功:0

                                  出错:-1

       当遇到void *stringManager( char *param )时,

Change it to:

void *stringManager( void *param );

        This will get rid of the warning. You can then typecast param to char pointer inside the function.

        pthread_exit函数的语法要点。

所需的头文件             #include <pthread.h>

函数原型                    void pthread_exit(void *value_ptr)

函数传入值                 value_ptr:线程退出时的返回值,可由其它函数pthread_join来检索获取。

        value_ptr是void 类型,和线程函数返回值的用法一样,其它线程可以调用pthread_join获得这个指针。

        需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

 

        pthread_join函数的语法要点。

所需的头文件              #include <pthread.h>

函数原型                    int pthread_join(pthread_t th , void **value_ptr)

函数传入值                 th:等待线程的标识符

                                 value_ptr:存储thread_exit函数的返回值。

返回值                       成功:0;失败:非0值,并设置errno。

        pthread_join会挂起调用该函数的线程,直到th参数指定的线程运行结束。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:

*       如果thread线程通过return返回,value_ptr所指向的单元里存放的是thread线程函数的返回值。

*       如果thread线程被别的线程调用pthread_cancel异常终止掉,value_ptr所指向的单元里存放的是常数PTHREAD_CANCELED。

*       如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。

 

eg1. 多线程编程:

/*thread.c*/

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>

void *thread1(void)
{
 int i=0;
 for(i=0;i<6;i++)
 {
  printf("This is a pthread1.\n");
  if(i==2)
   pthread_exit(0);
 }
}

void *thread2(void)
{
 int i;
 sleep(2);
 for(i=0;i<3;i++)
  printf("This is a pthread2.\n");
 pthread_exit(0);
}

int main(void)
{
 pthread_t id1,id2;
 int i,ret;
 /*create pthread1*/
 ret=pthread_create(&id1,NULL,(void *)thread1,NULL);
 if(ret!=0)
 {
  printf("Create pthread error!\n");
  exit(1);
 }

 /*create pthread2*/
 ret=pthread_create(&id2,NULL,(void *)thread2,NULL);
 if(ret!=0)
 {
  printf("Create pthread error!\n");
  exit(1);
 }

 /*waiting for the end of pthread*/
 pthread_join(id1,NULL);
 pthread_join(id2,NULL);
 exit(0);
}

$ gcc thread1.c -o test -lpthread

错误:

thread.c: warning: incompatible implicit declaration of built-in function ‘exit’

pthread.c:(.text+0x58): undefined reference to `pthread_create'

pthread.c:(.text+0xab): undefined reference to `pthread_join'

       在网上查了半天发现问题原因:

pthread 库不是 Linux 系统默认的库,连接时需要使用静态库 libpthread.a。

所以在使用pthread_create()创建线程,以及调用 pthread_atfork()函数建立fork处理程序时,需要链接该库。

 

解决:

       在编译中要加-lpthread参数

$ gcc thread1.c -o test -lpthread

 

解决:

       #include <stdlib.h> for a declaration of exit().

 

运行结果:

$ ./thread

This is a pthread1.

This is a pthread2.

This is a pthread2.

This is a pthread2.

This is a pthread1.

This is a pthread1.

 

$ ./thread

This is a pthread2.

This is a pthread2.

This is a pthread2.

This is a pthread1.

This is a pthread1.

This is a pthread1.

 

2. 修改线程属性

(1)函数说明

        在Linux系统中,每个线程都有一个与之相关的属性来表明该线程的特性。而一个线程的属性可能与多个线程相关,描述这些线程的共有属性。eg1中,调用pthread_create创建新的线程时,对第二个线程属性参数都指定了NULL,这表示创建的线程的属性取默认值。这些属性主要包括作用域属性、分离属性、堆栈地址、堆栈大小、优先级。其中系统默认的属性为作用在系统进程间、非分离、缺省1M的堆栈、与父进程同样级别的优先级。下面主要是作用域属性和分离属性的概念。

*       作用域属性

        表明线程调度范围是在同一个进程的所有线程之间,还是在系统所有进程间。

*       分离属性

        分离属性是用来决定一个线程以什么样的方式来终止自己。在非分离情况下,当一个线程结束时,它所占用的系统资源并没有被释放,也就是没有真正的终止。只有当pthread_join()函数返回事,创建的线程才能释放自己占用的系统资源。而在分离属性情况下,一个线程结束时立即释放它所占用的系统资源。这里要注意的一点是,如果设置一个线程的分离属性,而这个线程运行又非常快,那么它很可能在pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这时调用pthread_create的线程就得到了错误的进程号。

        这些属性的设置都是通过一定的函数来完成的,通常首先调用pthread_attr_init函数进行初始化,之后再调用相应的属性设置函数。设置绑定属性的函数为pthread_attr_setscope,设置线程分离属性的函数为:pthread_attr_setdetachstate,设置线程优先级的相关函数为pthread_attr_getschedparam(获得线程优先级)和pthread_attr_setschedparam(设置线程优先级)。在设置完这些属性后,就可以调用pthread_create函数来创建线程了。在设置完这些属性后,就可以调用pthread_create函数来创建线程了。

 

(2)函数格式

        pthread_attr_init函数的语法要点。

所需头文件                   #include <pthread.h>

函数原型                      int pthread_attr_init(pthread_attr_t *attr)

函数传入值                   attr:线程属性

函数返回值                   成功:0;出错:非0值,并设置erron

 

        pthread_attr_setscope函数的语法要点。

所需头文件                   #include <pthread.h>

函数原型                      int pthread_attr_setscope(pthread_attr_t *attr , int scope)

函数传入值                   attr:线程属性

                                   scope:PTHREAD_SCOPE_SYSTEM:所有进程之间

                                               PTHREAD_SCOPE_PROCESS:当前进程的所有线程之间

函数传入值                   成功:0;出错:非0,并设置errno

 

        pthread_attr_setdetachstate函数的语法要点。

所需头文件                   #include <pthread.h>

函数原型                      int pthread_attr_setdetachstate(pthread_attr_t *attr , int detachstate)

函数传入值                   attr:线程属性

                                   detachstate:PTHREAD_CREATE_DETACHED:分离状态

                                                        PTHREAD_CREATE_JOINABLE:非分离状态

函数返回值                   成功:0;出错:非0,并设置errno

 

        pthread_attr_getschedparam函数的语法要点。

所需头文件                   #include <pthread.h>

函数原型                      int pthread_attr_getschedparam(pthread_attr_t *attr , struct sched_param *param)

函数传入值                   attr:线程属性

                                   param:线程优先级

函数返回值                   成功:0;出错:非0,并设置errno

        sched_param结构体在<sched.h>中有定义。对于SCHED_FIFO和SCHED_RR调度算法,只需设置sched_param结构体中的线程调度优先级sched_priority即可。

 

eg2. 多线程带属性编程:

#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <stdlib.h>

/*thread1*/
void thread1(void)
{
 int i=0;
 for(i=0;i<6;i++)
 {
  printf("This is a pthread1.\n");
  if(i==2)
   pthread_exit((void *)0);
  sleep(1);
 }
}

/*thread2*/
void thread2(void)
{
 int i;
 while(1)
 {
  for(i=0;i<3;i++)
   printf("This is a pthread2.\n");
  sleep(1);
 }
 pthread_exit((void *)0);
}

int main(void)
{
 pthread_t id1,id2;
 int i,ret;
 pthread_attr_t attr;

 /*init pthread*/
 pthread_attr_init(&attr);
 /*set the zone attr of pthread*/
 pthread_attr_setscope(&attr,PTHREAD_SCOPE_SYSTEM);
 /*set the detached attr of pthread*/
 pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
 /*create pthread*/
 ret=pthread_create(&id1,&attr,(void *)thread1,NULL);
 if(ret!=0)
 {
  printf("Create pthread error!\n");
  exit(1);
 }
 ret=pthread_create(&id2,NULL,(void *)thread2,NULL);
 if(ret!=0)
 {
  printf("Create pthread error!\n");
  exit(1);
 }
 pthread_join(id1,NULL);
 pthread_join(id2,NULL);
 return 0;
}

 

3. 线程访问控制

        由于线程共享进程的资源和地址空间,因此在对这些资源进行操作时,必须要考虑到线程间资源访问的惟一行问题,这里主要介绍POSIX中线程同步的方法,主要有互斥锁和信号量的方式。

 

3.1 mutex互斥锁线程控制

(1)函数说明

        mutex是一种简单的加锁的方法来控制对共享资源的存取。这个互斥锁只有两种状态,也就是上锁和解锁。

        在同一时刻只能有一个线程掌握某个互斥锁,拥有上锁状态的线程能够对共享资源进行操作。若其他线程希望上锁一个已经上锁了的互斥锁,则该线程就会挂起,直到上锁的线程释放掉互斥锁为止。

       互斥锁的操作主要包括以下几个步骤:

*      互斥锁初始化:pthread_mutex_init

*      互斥锁上锁:pthread_mutex_lock

*      互斥锁上锁,但不阻塞:pthread_mutex_trylock

*      互斥锁解锁:pthread_mutex_unlock

*      消除互斥锁:pthread_mutex_destroy

*      条件变量初始化:int pthread_cond_init(pthread_cond_t *restrict cond , const pthread_condattr_t *restrict attr);

*      使线程阻塞在条件变量cond上,线程自动解锁mutex;成功返回之后,mutex被调用该函数的线程上锁:int pthread_cond_wait(pthread_cond_t *restrict cond , pthread_mutex_t *restrict mutex);

        返回值:成功返回0,失败返回错误号。

        可见,一个Condition Variable总是和一个Mutex搭配使用的。一个线程可以调用pthread_cond_wait在一个Condition Variable上阻塞等待,这个函数做以下三步操作:

a)释放Mutex;b)阻塞等待;c)当被唤醒时,重新获得Mutex并返回

 

* 同上,在给定时间过后,线程自动解锁;成功返回之后,mutex被调用该函数的线程上锁:int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);

* 释放阻塞在变量con上的线程,多个线程时,由调度器决定先后顺序:int pthread_cond_signal(pthread_cond_t *cond);

 

(2)函数格式

 pthread_mutex_init函数语法要点

所需头文件                   #include <pthread.h>

函数原型                      int pthread_mutex_init(pthread_mutex_t *restrict mutex , const pthread_mutexattr_t *restrict attr);

                                   pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //等同于令attr=NULL

函数传入值                   mutex:互斥锁

函数返回值                   成功:0;出错:非0,并设置errno

The pthread_mutex_init() function shall fail if:

EAGAIN

       The system lacked the necessary resources (other than memory) to initialize another mutex.

ENOMEM

       Insufficient memory exists to initialize the mutex.

EPERM

       The caller does not have the privilege to perform the operation.

 

       pthread_mutex_lock函数语法要点

所需头文件                       #include <pthread.h>

函数原型                          int pthread_mutex_lock(pthread_mutex_t *mutex);

                                        int pthread_mutex_trylock(pthread_mutex_t *mutex);

                                        int pthread_mutex_unlock(pthread_mutex_t *mutex);

                                        int pthread_mutex_destroy(pthread_mutex_t *mutex);

函数传入值                       mutex:互斥锁

                     PTHREAD_MUTEX_NORMAL:不提供死锁检测,试图锁一个已上锁的mutex将导致死锁。如果线程试图解锁一个未上锁的mutex会出现未知结果。

                     PTHREAD_MUTEX_ERRORCHECK:提供错误检测,线程试图上锁已上锁的mutex或解锁未上锁的mutex会返回错误。

                     PTHREAD_MUTEX_RECURSIVE:带有锁计数器,当一个线程第一次成功获取mutex时,锁计数器被置为一,每次线程再次对该mutex上锁时,锁计数器会增加一,每次线程对mutex解锁时,锁计数器会减少一。当锁计数器为0时,mutex才能被其它线程获取。当一个线程试图去解锁一个没被上锁或已被解锁的mutex时,将返回错误。

                     PTHREAD_MUTEX_DEFAULT:试图递归地对mutex上锁将导致未知结果;试图去解锁并未由调用中的线程上锁的mutex将导致未知结果;试图去解锁未上锁的mutex将导致未知结果。

函数返回值                       成功:0;出错:非0,并设置errno

 

errno类型 

       The pthread_mutex_lock() and pthread_mutex_trylock() functions shall fail if:

EINVAL

       The mutex was created with the protocol attribute having the value PTHREAD_PRIO_PROTECT and the calling thread's priority is higher than the mutex's current priority ceiling.

 

The pthread_mutex_trylock() function shall fail if:

 

EBUSY

       The mutex could not be acquired because it was already locked.

       The pthread_mutex_lock(), pthread_mutex_trylock(), and pthread_mutex_unlock() functions may fail if:

EINVAL

       The value specified by mutex does not refer to an initialized mutex object.

EAGAIN

       The mutex could not be acquired because the maximum number of recursive locks for mutex has been exceeded.

 

       The pthread_mutex_lock() function may fail if:

EDEADLK

       The current thread already owns the mutex.

 

The pthread_mutex_unlock() function may fail if:

EPERM

       The current thread does not own the mutex.

 

       The pthread_mutex_destroy() function may fail if:

EBUSY

       The implementation has detected an attempt to destroy the object referenced by mutex while it is locked or referenced (for example, while being used in a pthread_cond_timedwait() or pthread_cond_wait()) by another thread.

 

EINVAL

       The value specified by mutex is invalid.

 

eg3. 使用互斥锁来实现对变量lock_var的加一、打印操作

//mutex.c
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;//初始化互斥锁
int lock_var;//计数变量
time_t end_time;

void *pthread1(void *arg);
void *pthread2(void *arg);

int main(int argc , char argv[])
{
    pthread_t id1,id2;
    int ret;
    end_time=time(NULL)+10;

    pthread_mutex_init(&mutex,NULL);
    ret=pthread_create(&id1,NULL,(void *)pthread1,NULL);
    if(ret!=0)
        perror("pthread create1");

    ret=pthread_create(&id2,NULL,(void *)pthread2,NULL);
    if(ret!=0)
        perror("pthread create2");

    pthread_join(id1,NULL);
    pthread_join(id2,NULL);
    exit(0);
}

/*pthread1*/
void *pthread1(void *arg)
{
    int i;
    while(time(NULL)<end_time)
    {
        if(pthread_mutex_lock(&mutex)!=0)
        {
            perror("pthread_mutex_lock");
        }
        else
            printf("pthread1: pthread1 lock the variable\n");
        for(i=0;i<2;i++)
        {
            sleep(1);
            lock_var++;
        }

        if(pthread_mutex_unlock(&mutex)!=0)
            perror("pthread_mutex_unlock");
        else
            printf("pthread1: pthread1 unlock the variable\n");
        sleep(1);
    }
}

/*pthread2*/
void *pthread2(void *arg)
{
    int ret;
    while(time(NULL)<end_time)
    {
        ret=pthread_mutex_trylock(&mutex);
        if(ret==EBUSY)
            printf("pthread2: the variable is locked by pthread1\n");
        else
        {
            if(ret!=0)
            {
                perror("pthread_mutex_trylock");
                exit(1);
            }
            else
                printf("pthread2: pthread2 got lock. The variable is %d\n",lock_var);
            if(pthread_mutex_unlock(&mutex)!=0)
                perror("pthread_mutex_unlock");
            else
                printf("pthread2: pthread2 unlock the variable\n");
        }
        sleep(3);
    }
}

        运行结果:

 

$ gcc mutex.c -o mutex -lpthread

$ ./mutex

 

3.2 信号量线程控制

(1)信号量说明

       信号量也就是操作系统中所用到的PV原语,它广泛用于进程或线程间的同步与互斥。信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。

        一个信号量本质上是一个整数值,它和一对函数联合使用,这一对函数通常称为P和V。希望进入临界区的进程将在相关信号量上调用P,该函数对信号量进行减一操作,如果信号量的值大于等于0,而进程可以继续。相反,如果信号量的值小于0,进程将阻塞,必须等待直到其它进程释放该信号量。对信号量的解锁通过调用V完成,该函数对信号量进行加一操作,如果信号量的值小于等于0,唤醒等待进程队列中的第一个进程。

        当信号量用于互斥时(即避免多个进程同时在一个临界区中运行),信号量的值应当初始化为1。这种信号量在任何给定时刻只能由单个进程或线程拥有。

 

(2)函数说明

* sem_init用于创建一个信号量,并能初始化它的值。

* sem_wait和sem_trywait相当于P操作,它们都能将信号量的值减一,两者的区别在于若信号量小于零时,sem_wait将会阻塞进程,而sem_trywait则会立即返回。

* sem_post相当于V操作,它将信号量的值加一同时发出信号,唤醒等待的进程。

* sem_getvalue用于得到信号量的值。

* sem_destroy用于删除信号量。

       sem_init函数的语法要点

所需头文件                #include <semaphore.h>

函数原型                   int sem_init(sem_t *sem, int pshared, unsigned int value);

函数传入值                sem:信号量

                                 pshared:决定信号量是在一个进程的两个线程之间还是在两个进程之间共享。

                                 value:信号量初始化值

       sem_wait函数的语法要点

所需头文件                #include <semaphore.h>

函数原型                   int sem_wait(sem_t *sem);

                                 int sem_trywait(sem_t *sem);

                                 int sem_post(sem_t *sem);

                                 int sem_getvalue(sem_t *sem, int *sval);

                                 int sem_destroy(sem_t *sem);

函数传入值                sem:信号量

                                 sval:用于复制sem的值

函数返回值                成功:0;失败:-1,并设置errno

 

eg4. 使用信号量实现eg3中对lock_var的操作,在这里使用的是互斥操作,也就是只使用一个信号量来实现。

//sem.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <sys/ipc.h>
#include <semaphore.h>

int lock_var;
time_t end_time;
sem_t sem;

void *pthread1(void *arg);
void *pthread2(void *arg);

int main(int argc , char *argv[])
{
    pthread_t id1,id2;
    int ret;
    end_time=time(NULL)+30;//延时为30s

    //初始化信号量为1
    ret=sem_init(&sem,0,1);
    if(ret!=0)
        perror("sem_init");

    //创建两个线程
    ret=pthread_create(&id1,NULL,(void *)pthread1,NULL);
    if(ret!=0)
        perror("pthread create1");

    ret=pthread_create(&id2,NULL,(void *)pthread2,NULL);
    if(ret!=0)
        perror("pthread create2");
    pthread_join(id1,NULL);
    pthread_join(id2,NULL);
    exit(0);
}

void *pthread1(void *arg)
{
    int i;
    while(time(NULL)<end_time)
    {
    //P操作,使信号量减一
    sem_wait(&sem);
    for(i=0;i<2;i++)
    {
        sleep(1);
        lock_var++;
        printf("pthread1 got the lock:lock_var=%d\n",lock_var);
    }

    //V操作,使信号量加一
    sem_post(&sem);
    sleep(1);
    }
}

void *pthread2(void *arg)
{
    int ret;
    while(time(NULL)<end_time)
    {
        //P操作,使信号量减一
        sem_wait(&sem);
        printf("pthread2 got lock; lock_var=%d\n",lock_var);
        sem_post(&sem);
        sleep(3);
    }
}

    运行结果:

$ gcc sem.c -o sem -lpthread

$ ./sem

pthread2 got lock; lock_var=0

pthread1 got the lock : lock_var=1

pthread1 got the lock : lock_var=2

pthread2 got lock; lock_var=2

pthread1 got the lock : lock_var=3

pthread1 got the lock : lock_var=4

pthread2 got lock; lock_var=4

pthread1 got the lock : lock_var=5

pthread1 got the lock : lock_var=6

pthread2 got lock; lock_var=6

pthread1 got the lock : lock_var=7

pthread1 got the lock : lock_var=8

 

eg5. 生产者和消费者

//mutex_cons.c

/*************************************

 * The classic producer-consumer example.

 * Illustrates mutexes and conditions

 *************************************/

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define BUFFER_SIZE 16

/*设置一个整数的圆形缓冲区*/
struct products
{
    int buffer[BUFFER_SIZE]; /*缓冲区数组*/
    pthread_mutex_t lock; /*互斥锁*/
    int readpos,writepos; /*读写的位置*/
    pthread_cond_t notempty; /*缓冲区非空信号*/
    pthread_cond_t notfull;  /*缓冲区非满信号*/
};

/*初始化缓冲区*/
void init_buffer(struct products *b)
{
    pthread_mutex_init(&b->lock,NULL);
    pthread_cond_init(&b->notempty,NULL);
    pthread_cond_init(&b->notfull,NULL);
    b->readpos=0;
    b->writepos=0;
}

/*向缓冲区中写入一个整型数*/
void write(struct products *b , int data )
{
    pthread_mutex_lock(&b->lock);
    /*等待缓冲区非满*/
    while((b->writepos+1)%BUFFER_SIZE==b->readpos)
    {
        printf("wait for not full\n");
        pthread_cond_wait(&b->notfull,&b->lock);
    }

    /*写数据并且指针前移*/
    b->buffer[b->writepos]=data;
    b->writepos++;
    if(b->writepos>=BUFFER_SIZE)
        b->writepos=0;

    /*设置缓冲区非空信号*/
    pthread_cond_signal(&b->notempty);
    pthread_mutex_unlock(&b->lock);
}

/*从缓冲区中读出一个整型数*/
int read(struct products *b)
{
    int data;
    pthread_mutex_lock(&b->lock);

    /*等待缓冲区非空*/
    while(b->writepos==b->readpos)
    {
        printf("wait for not empty\n");
        pthread_cond_wait(&b->notempty,&b->lock);
    }

    /*读数据并且指针前移*/
    data=b->buffer[b->readpos];
    b->readpos++;
    if(b->readpos>=BUFFER_SIZE)
        b->readpos=0;
    pthread_cond_signal(&b->notfull);
    pthread_mutex_unlock(&b->lock);
    return data;
}

#define OVER (-1)
struct products buffer_data;

void *producter(void *data)
{
    int n;
    for(n=0;n<50;n++)
    {
        printf("write-->%d\n",n);
        write(&buffer_data,n);
    }

    write(&buffer_data,OVER);
    printf("producter stopped\n");
    return NULL;
}

void *consumer(void *data)
{
    int d;
    while(1)
    {
        d=read(&buffer_data);
        if(d==OVER)
            break;
        printf("    %d-->read\n",d);
    }
    printf("consumer stopped!\n");
    return NULL;
}

int main(void)
{
    pthread_t th_a,th_b;
    void *retval;
    int ret;
    init_buffer(&buffer_data);

    //创建两个线程
    ret=pthread_create(&th_a,NULL,(void *)producter,NULL);
    if(ret!=0)
    {
        perror("pthread producter create");
        exit(1);
    }

    ret=pthread_create(&th_b,NULL,(void *)consumer,NULL);
    if(ret!=0)
    {
        perror("pthread consumer create");
        exit(1);
    }

    pthread_join(th_a,&retval);
    pthread_join(th_b,&retval);
    exit(0);
}

 

    运行结果:

$ ./thread

wait for not empty

write-->0

write-->1

write-->2

write-->3

write-->4

write-->5

write-->6

write-->7

write-->8

write-->9

write-->10

write-->11

write-->12

write-->13

write-->14

write-->15

wait for not full

    0-->read

    1-->read

    2-->read

    3-->read

    4-->read

    5-->read

    6-->read

    7-->read

    8-->read

    9-->read

    10-->read

    11-->read

    12-->read

    13-->read

    14-->read

wait for not empty

write-->16

write-->17

write-->18

write-->19

write-->20

write-->21

write-->22

write-->23

write-->24

write-->25

write-->26

write-->27

write-->28

write-->29

write-->30

wait for not full

    15-->read

    16-->read

    17-->read

    18-->read

    19-->read

    20-->read

    21-->read

    22-->read

    23-->read

    24-->read

    25-->read

    26-->read

    27-->read

    28-->read

    29-->read

wait for not empty

write-->31

write-->32

write-->33

write-->34

write-->35

write-->36

write-->37

write-->38

write-->39

write-->40

write-->41

write-->42

write-->43

write-->44

write-->45

wait for not full

    30-->read

    31-->read

    32-->read

    33-->read

    34-->read

    35-->read

    36-->read

    37-->read

    38-->read

    39-->read

    40-->read

    41-->read

    42-->read

    43-->read

    44-->read

wait for not empty

write-->46

write-->47

write-->48

write-->49

producter stopped

    45-->read

    46-->read

    47-->read

    48-->read

    49-->read

consumer stopped!


//thread_join.c--线程传入值和传出值的用法
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>

void *thread(void *str)
{
    int i;
    for (i=0;i<(int)str;i++)
    {
        sleep(1);
        printf( "This in the thread : %d\n" , i );
    }
    return (void *)8;
}

int main()
{
    pthread_t pth;
    int i=3;
    int ret;
 void *pr;
 ret=pthread_create(&pth, NULL, thread, (void *)(i));
 if(ret!=0)
 {
    printf("create thread failed!\n");
    return -1;
 }

    pthread_join(pth,&pr);
    printf("The new thread's exit code is: %d\n",(int)pr);
    printf("123\n");
    for (i=0;i<3;i++)
    {
        //sleep(1);
        printf("This in the main: %d\n",i);
    }

    return 0;
}

4. 线程清理函数
#include <pthread.h>
void pthread_cleanup_push(void (*routine)(void *),void *arg);
void pthread_cleanup_pop(int execute);
功能:这些函数操作调用线程的线程清理处理堆栈。清理处理函数的功能是当线程被取消(或其他情况)时,它能自动地执行。比如,解锁一个互斥锁,让进程中的其他线程使用该锁。
    pthread_cleanup_push()函数将routine压入清理函数栈的栈顶。当调用routine时,使用arg作为参数。
    pthread_cleanup_pop()函数将routine弹出栈,并可根据execute是否非零来决定清理函数是否执行。
    如下情况下,清理函数会弹出栈并被执行:
1)当线程被取消时,所以的清理函数以它们入栈的反顺序出栈并被执行。
2)当线程调用pthread_exit终止时,操作同上。(当线程执行return返回时,并执行清理函数)
3)当线程调用带非零execute参数的pthread_cleanup_pop() 函数时,最顶层的清理函数被弹出并执行。

//thread_clean.c---thread_cleanup_push和pthread_cleanup_pop函数的用法
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void *clean(void *arg)
{
    printf("cleanup :%s  \n",(char *)arg);
    return (void *)0;
}

void *thr_fn1(void *arg)
{
    printf("thread 1 start\n");
    pthread_cleanup_push((void*)clean,"thread 1 first handler");
    pthread_cleanup_push((void*)clean,"thread 1 second hadler");
    printf("thread 1 push complete\n");
    if(arg)
    {
        return((void *)1);
    }
    pthread_cleanup_pop(0);
    pthread_cleanup_pop(0);
    return (void *)1;
}

void *thr_fn2(void *arg)
{
    printf("thread 2 start\n");
    pthread_cleanup_push( (void*)clean,"thread 2 first handler");
    pthread_cleanup_push( (void*)clean,"thread 2 second handler");
    printf("thread 2 push complete\n");
    if(arg)
    {
        pthread_exit((void *)2);
    }
    pthread_cleanup_pop(0);
    pthread_cleanup_pop(0);
    pthread_exit((void *)2);
}

int main(void)
{
    int err;
    pthread_t tid1,tid2;
    void *tret;

    err=pthread_create(&tid1,NULL,thr_fn1,(void *)1);
    if(err!=0)
    {
        printf("error....\n");
        return -1;
    }
    err=pthread_create(&tid2,NULL,thr_fn2,(void *)1);
    if(err!=0)
    {
        printf("error....\n");
        return -1;
    }

    err=pthread_join(tid1,&tret);
    if(err!=0)
    {
        printf("error ....\n");
        return -1;
    }
    printf("thread 1 exit code %d\n",(int)tret);

    err=pthread_join(tid2,&tret);
    if(err!=0)
    {
        printf("error....");
        return -1;
    }
    printf("thread 2 exit code %d\n",(int)tret);

    return 1;
}

Reference

1. http://www.cnblogs.com/feefk/archive/2009/12/09/1620557.html

2. http://stackoverflow.com/questions/1662909/undefined-reference-to-pthread-create-in-linux-c-programming
国嵌视频

  评论这张
 
阅读(1639)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017