Threads 的基本使用方法

POSIX Threads

1.1      Thread基本要素

ž   线程操作包括创、终止、同步(joins,blocking)、调度、数据管理、过程交互。

ž   一个线程不维持一个已创建线程列表,不知道哪些线程已经创建。

ž   在一个进程的所有线程共享相同的地址空间。

ž   同一个进程的线程共享:Process instructions,Most data,open files,signals and handlers,current working directory,user and group id

ž   每个线程拥有不同的Thread ID,set of registers,stack pointer,stack for local variables,return addresses,signal mask,priority, return value(errno)

ž   Pthreads函数返回0表示成功。

1.2      Thread 创建和终止

Examplepthread1.c

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

void *print_message_function( void *ptr );

main()
{
     pthread_t thread1, thread2;
     const char *message1 = "Thread 1";
     const char *message2 = "Thread 2";
     int  iret1, iret2;

    /*创建两个独立的线程,并且都执行同一个函数 */
     iret1 = pthread_create( &thread1, NULL, print_message_function, (void*) message1);
     iret2 = pthread_create( &thread2, NULL, print_message_function, (void*) message2);
     /*等待线程1和线程2结束*/
     pthread_join( thread1, NULL);
     pthread_join( thread2, NULL); 

     printf("Thread 1 returns: %d\n",iret1);
     printf("Thread 2 returns: %d\n",iret2);
     exit(0);
}

void *print_message_function( void *ptr )
{
     char *message;
     message = (char *) ptr;
     printf("%s \n", message);
}

 

编译:gcc –o  pthread1  –lpthread  pthread1.c

运行:./pthread1

结果:Thread 1
Thread 2
Thread 1 returns: 0
Thread 2 returns: 0

ž   线程的终止可以显示的调用pthread_exit或让回调函数返回,或调用exit来终止进程以及包括的所有线程。

ž   函数调用:pthread_create创建新的线程

  int  pthread_create(pthread_t *thread,

                  const pthread_attr_t * attr,

                  void * (*start_routine)(void *),

                  void *arg);

参数:

thread:返回线程的ID,类型为unsigned long int,定义在bits/pthreadtypes.h中。

attr    :如果使用默认的属性则设为NULL,否则需要定义结构pthread_attr_tbits/pthreadtypes.h)的成员。属性值见Man

void * (*start_routine)指向线程的入口函数,包含一个指向void型的指针。

*arg:函数的参数,传递多参数时,用指向结构体的指针。

ž   函数调用:pthread_join等待另外一个线程终止

  int  pthread_join(pthread_t  th,

                void **thread_return);

参数:

     th:线程ID  

 thread_return:如果该值不为空,返回值将存储在thread_return指向的地址。

ž   函数调用:pthread_exit终止调用的线程

 

void  pthread_exit(void *retval);

参数:retval:线程的返回值。该变量不能是局部变量。

该函数将kill一个线程。

注明:对于C,采用下面的方式传入口函数。

void print_message_function( void *ptr );
...
...
iret1 = pthread_create( &thread1, NULL, (void*)&print_message_function, (void*) message1);
...
...

1.3      Thread 同步

P_thread库提供了三种线程同步机制:mutexes(互斥锁)joins(让一个线程等待另外一个线程结束)、condition variables (条件变量,类型为pthread_cond_t)

Mutexes

主要用来保护内存数据的一致性以及防止资源竞争,当多个线程同时操作同一块内存中的数据时,将导致数据不一致。Mutexes只能在单个进程中的多个线程中使用,不能在不同的进程中使用,但是semaphores可以在不同的进程中使用。

无锁

 int counter = 0;

void functionC()

{

counter++;

}

 

有锁

/*变量和锁的范围是相同的*/

/*变量和锁的范围是相同的*/
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALZER;
int counter = 0;
void function()
{
pthread_mutex_lock(&mutex1);
counter++;
pthread_mutex_unlock(&mutex1);
}


 

Example:mutex1.c

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

void *functionC();
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;//锁的初始化
int  counter = 0;

main()
{
   int rc1, rc2;
   pthread_t thread1, thread2;
   if( (rc1=pthread_create( &thread1, NULL, &functionC, NULL)) )
   {
      printf("Thread creation failed: %d\n", rc1);
   }

   if( (rc2=pthread_create( &thread2, NULL, &functionC, NULL)) )
   {
      printf("Thread creation failed: %d\n", rc2);
   }

   pthread_join( thread1, NULL);
   pthread_join( thread2, NULL); 

   exit(0);
}

void *functionC()
{
   pthread_mutex_lock( &mutex1 );
   counter++;
   printf("Counter value: %d\n",counter);
   pthread_mutex_unlock( &mutex1 );
}


 

编译:gcc –o  mutex1  –lpthread  mutex1.c

运行结果:

Counter value: 1 Counter value: 2
 
example :join1.c
include <stdio.h>
#include <pthread.h>

#define NTHREADS 10
void *thread_function(void *);
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
int  counter = 0;

main()
{
   pthread_t thread_id[NTHREADS];
   int i, j;

   for(i=0; i < NTHREADS; i++)
   {
      pthread_create( &thread_id[i], NULL, thread_function, NULL );
   }

   for(j=0; j < NTHREADS; j++)
   {
      pthread_join( thread_id[j], NULL); 
   }
  
   /* 等所有的线程都执行结束 再打印*/

   printf("Final counter value: %d\n", counter);
}

void *thread_function(void *dummyPtr)
{
   printf("Thread number %ld\n", pthread_self());
   pthread_mutex_lock( &mutex1 );
   counter++;
   pthread_mutex_unlock( &mutex1 );
}

编译:cc -pthread join1.c or cc -lpthread join1.c
运行结果:
Thread number 1026
Thread number 2051
Thread number 3076
Thread number 4101
Thread number 5126
Thread number 6151
Thread number 7176
Thread number 8201
Thread number 9226
Thread number 10251
Final counter value: 10

条件变量pthread_cond_t

条件变量可以让线程中止执行,并且让出CPU直到条件变量为真。条件变量要和锁(没有规定是什么锁)一起使用,防止出现临界资源竞争。

  • Creating/Destroying:
    • pthread_cond_init初始化
    • pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    • pthread_cond_destroy销毁
  • Waiting on condition:
    • pthread_cond_wait解锁并等待条件发生
    • pthread_cond_timedwait设置条件阻塞的时间
  • Waking thread based on condition:
    • pthread_cond_signal -启动在等条件变量的线程
    • pthread_cond_broadcast唤醒所有被该条件变量阻塞的线程
Example code:cond.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

//初始化锁和条件变量
pthread_mutex_t count_mutex     = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  condition_var   = PTHREAD_COND_INITIALIZER;

void *functionCount1();
void *functionCount2();
int  count = 0;
#define COUNT_DONE  10
#define COUNT_HALT1  3
#define COUNT_HALT2  6

main()
{
   pthread_t thread1, thread2;
   //创建两个线程并设置入口函数
   pthread_create( &thread1, NULL, &functionCount1, NULL);
   pthread_create( &thread2, NULL, &functionCount2, NULL);
   //等待两个线程结束
   pthread_join( thread1, NULL);
   pthread_join( thread2, NULL);

   printf("Final count: %d\n",count);

   exit(0);
}

// 根据函数functionCount2()设置的条件变量,来写数字1-3和8-10

void *functionCount1()
{
   for(;;)
   {
      // Lock mutex and then wait for signal to relase mutex
      pthread_mutex_lock( &count_mutex );

     
      //解锁count_mutex,并等条件变量的发生
      pthread_cond_wait( &condition_var, &count_mutex );
      count++;
      printf("Counter value functionCount1: %d\n",count);

      pthread_mutex_unlock( &count_mutex );

      if(count >= COUNT_DONE) return(NULL);
    }
}

// 写数字4-7

void *functionCount2()
{
    for(;;)
    {
       pthread_mutex_lock( &count_mutex );

       if( count < COUNT_HALT1 || count > COUNT_HALT2 )
       {
         
          //条件满足,给等待的线程发信号,并且释放锁
          pthread_cond_signal( &condition_var );
       }
       else
       {
          count++;
          printf("Counter value functionCount2: %d\n",count);
       }

       pthread_mutex_unlock( &count_mutex );

       if(count >= COUNT_DONE) return(NULL);
    }

}

编译:cc -pthread cond1.c or cc -lpthread cond1.c
运行结果:
Counter value functionCount1: 1
Counter value functionCount1: 2
Counter value functionCount1: 3
Counter value functionCount2: 4
Counter value functionCount2: 5
Counter value functionCount2: 6
Counter value functionCount2: 7
Counter value functionCount1: 8
Counter value functionCount1: 9
Counter value functionCount1: 10
Final count: 10

线程陷阱

1.       条件竞争:在多线程环境中,可能代码的执行顺序不是按照书写的顺序执行,由于操作系统的调度,各个线程的执行顺序不同,并且各个线程执行的速度也不同,会导致不可预期的结果。

2.       线程安全代码:对于静态变量和全局变量的访问必须要加锁,禁止使用不可重入的函数。如strtok函数,要使用线程安全的函数strtok_r.

3.       线程死锁:当使用两把或更多把锁的时候要防止死锁。可以使用下面的方法来防止死锁。

...
pthread_mutex_lock(&mutex_1);
while ( pthread_mutex_trylock(&mutex_2) )  /* Test if already locked   */
{
   pthread_mutex_unlock(&mutex_1);  /* Free resource to avoid deadlock */
   ...
   /* stall here   */
   ...
   pthread_mutex_lock(&mutex_1);
}
count++;
pthread_mutex_unlock(&mutex_1);
pthread_mutex_unlock(&mutex_2);
...

你可能感兴趣的:(多线程,死锁,条件变量,竞争)