Pthread多线程

一、基本接口介绍:
数据类型
pthread_t:线程ID
pthread_attr_t:线程属性

操纵函数
pthread_create():创建一个线程
pthread_exit():终止当前线程
pthread_cancel():中断另外一个线程的运行
pthread_join():阻塞当前的线程,直到另外一个线程运行结束
pthread_attr_init():初始化线程的属性
pthread_attr_setdetachstate():设置脱离状态的属性(决定这个线程在终止时是否可以被结合)
pthread_attr_getdetachstate():获取脱离状态的属性
pthread_attr_destroy():删除线程的属性
pthread_kill():向线程发送一个信号

同步函数
用于 mutex 和条件变量
pthread_mutex_init() 初始化互斥锁
pthread_mutex_destroy() 删除互斥锁
pthread_mutex_lock():占有互斥锁(阻塞操作)
pthread_mutex_trylock():试图占有互斥锁(不阻塞操作)。即,当互斥锁空闲时,将占有该锁;否则,立即返回。
pthread_mutex_unlock(): 释放互斥锁
pthread_cond_init():初始化条件变量
pthread_cond_destroy():销毁条件变量
pthread_cond_signal(): 唤醒第一个调用pthread_cond_wait()而进入睡眠的线程
pthread_cond_wait(): 等待条件变量的特殊条件发生
Thread-local storage(或者以Pthreads术语,称作线程特有数据):
pthread_key_create(): 分配用于标识进程中线程特定数据的键
pthread_setspecific(): 为指定线程特定数据键设置线程特定绑定
pthread_getspecific(): 获取调用线程的键绑定,并将该绑定存储在 value 指向的位置中
pthread_key_delete(): 销毁现有线程特定数据键
pthread_attr_getschedparam();获取线程优先级
pthread_attr_setschedparam();设置线程优先级

工具函数
pthread_equal(): 对两个线程的线程标识号进行比较
pthread_detach(): 分离线程
pthread_self(): 查询线程自身线程标识号

二、线程属性
pthread_create()中的attr参数是一个结构指针,结构中的元素分别对应着新线程的运行属性,主要包括以下几项:

__detachstate,表示新线程是否与进程中其他线程脱离同步,如果置位则新线程不能用pthread_join()来同步,且在退出时自行释放所占用的资源。缺省为PTHREAD_CREATE_JOINABLE状态。这个属性也可以在线程创建并运行以后用pthread_detach()来设置,而一旦设置为PTHREAD_CREATE_DETACH状态(不论是创建时设置还是运行时设置)则不能再恢复到 PTHREAD_CREATE_JOINABLE状态。

__schedpolicy,表示新线程的调度策略,主要包括SCHED_OTHER(正常、非实时)、SCHED_RR(实时、轮转法)和 SCHED_FIFO(实时、先入先出)三种,缺省为SCHED_OTHER,后两种调度策略仅对超级用户有效。运行时可以用过 pthread_setschedparam()来改变。

__schedparam,一个struct sched_param结构,目前仅有一个sched_priority整型变量表示线程的运行优先级。这个参数仅当调度策略为实时(即SCHED_RR或SCHED_FIFO)时才有效,并可以在运行时通过pthread_setschedparam()函数来改变,缺省为0。

__inheritsched,有两种值可供选择:PTHREAD_EXPLICIT_SCHED和PTHREAD_INHERIT_SCHED,前者表示新线程使用显式指定调度策略和调度参数(即attr中的值),而后者表示继承调用者线程的值。缺省为PTHREAD_EXPLICIT_SCHED。

__scope,表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。POSIX的标准中定义了两个值: PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示与系统中所有线程一起竞争CPU时间,后者表示仅与同进程中的线程竞争CPU。目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。

pthread_attr_t结构中还有一些值,为了设置这些属性,POSIX定义了一系列属性设置函数,包括pthread_attr_init()、 pthread_attr_destroy()和与各个属性相关的pthread_attr_get(),pthread_attr_set()函数。

pthread_create()中,第二个参数(pthread_attr_t)为将要创建的thread属性。通常情况下配置为NULL,使用缺省设置就可以了。但了解这些属性,有利于更好的理解thread.

属性对象(pthread_attr_t)是不透明的,而且不能通过赋值直接进行修改。系统提供了一组函数,用于初始化、配置和销毁每种对象类型。

数据运用
函数应用背景:在单线程程序中,函数经常使用全局变量或静态变量,这是不会影响程序的正确性的,但如果多线程调用的函数使用全局变量或静态变量,则很可能引起编程错误,因为这些函数使用的全局变量和静态变量无法为不同的线程保存各自的值,而当同一进程内的不同线程几乎同时调用这样的函数时就可能会有问题发生。而解决这一问题的一种方式就是使用线程特定数据的机制。
下面我们引入一个简单程序实例,并以此作为介绍线程特定数据的案例。 [2]
线程特定数据:
#include <stdio.h>
#include <string.h>

static char str[100];

void A(char* s)
{
strncpy(str,s,100);
}

void B()
{
printf("%s\n",str);
}
可以想象,如果在多线程程序中,各个线程都依次调用函数 A 和函数 B,那么某些线程可能得不到期望的显示结果,因为它使用 B 显示的字符串可能并不是在 A 中设置的字符串。读者会发现,这两个函数非常的简单,但在本章内容中,这两个函数已经足以解释线程特定数据的含义,因为这两个函数代表了使用线程特定数据机制的一种典型场合,即有多个函数使用同一个全局变量。
POSIX要求实现POSIX的系统为每个进程维护一个称之为 Key 的结构数组,这个数组中的每一个结构称之为一个线程特定数据元素。POSIX 规定系统实现的 Key 结构数组必须包含不少于 128 个线程特定数据元素,而每个线程特定数据元素中至少包含两项内容:使用标志和析构函数指针。线程特定数据元素中的使用标志指示这个数组元素是否正在使用,初始值为“不在使用”,我们稍后讨论线程特定数据元素中的析构函数指针。在后面的介绍中,我们假设Key 结构数组中包含 128 个元素。 [3]
Key 结 构 数 组 中 每 个 元 素 的 索 引 (0~127) 称 之 为 键 (key) 当 一 个 线 程 调 用,pthread_key_create 创建一个新的线程特定数据元素时,系统搜索其所在进程的 Key 结构数组,找出其中第一个不在使用的元素,并返回该元素的键。这个函数的形式为:
1 int pthread_key_create(pthread_key_t *key, void (destructor)(void));
[4]
参数 key 为一个 pthread_key_t变量的指针,用于保存得到的键值。参数 destructor为指定的析构函数的指针。除了 Key 结构数组,系统还在进程中维护关于每个线程的多种信息。这些特定于线程的信息被保存于称之为 Pthread 的结构中。Pthread 结构中包含名为 pkey 的指针数组,其长度为128,初始值为空。这 128 个指针与 Key 结构数组的 128 个线程特定数据元素一一对应。在调用 pthread_key_create 得到一个键之后,每个线程可以依据这个键操作自己的 pkey 指针数组中对应的指针,这通过 pthread_getspecific 和 pthread_setspecific 函数来实现。这两个函数的形式为:
1
2 void *pthread_getspecific(pthread_key_t key);
int pthread_setspecific(pthread_key_t key, const void *value);
[5]
pthread_getspecific 返回 pkey 中对应于 key 的指针,而 pthread_setspecific 将 pkey 中对应于 key 的指针设置为 value。我们使用线程特定数据机制,就是要使线程中的函数可以共享一些数据。如果我们在线程中通过 malloc 获得一块内存,并把这块内存的指针通过 pthread_setspecific 设置到 pkey指针数组中对应于 key 的位置,那么线程中调用的函数即可通过 pthread_getspecific 获得这个指针,这就实现了线程内部数据在各个函数间的共享。当一个线程终止时,系统将扫描该线程的 pkey数组,为每个非空的 pkey指针调用相应的析构函数,因此只要将执行回收的函数的指针在调用 pthread_key_create 时作为函数的参数,即可在线程终止时自动回收分配的内存区。

几个测试程序:
1.
#include <stdio.h>
#include <Windows.h>
#include “pthread.h”

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;/初始化互斥锁/
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;/初始化条件变量/

void *thread1(void *);
void *thread2(void *);

int i=1;
int main(void)
{
pthread_win32_process_attach_np();
pthread_win32_thread_attach_np();

pthread_t t_a;
pthread_t t_b;

pthread_create(&t_a,NULL,thread2,(void *)NULL);/*创建进程t_a*/
pthread_create(&t_b,NULL,thread1,(void *)NULL); /*创建进程t_b*/
pthread_join(t_b, NULL);/*等待进程t_b结束*/
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);

pthread_win32_thread_detach_np();
pthread_win32_process_detach_np();

system("pause");
return 0;

}

void *thread1(void *junk)
{
for(i=1;i<=9;i++)
{

    if(i%3==0)
    {
        pthread_mutex_lock(&mutex);/*锁住互斥量*/
        pthread_cond_signal(&cond);/*条件改变,发送信号,通知t_b进程*/
        pthread_mutex_unlock(&mutex);/*解锁互斥量*/
    }
    else        
        printf("thead1:%d\n",i);

}

return NULL;

}

void *thread2(void *junk)
{
while(i<9)
{

    if(i%3!=0)
    {
        pthread_mutex_lock(&mutex);
        pthread_cond_wait(&cond,&mutex);/*等待*/
        pthread_mutex_unlock(&mutex);
    }
    printf("thread2:%d\n",i);
    

}

return NULL;

}
结果:
2.
#include <stdio.h>
#include <Windows.h>
#include “pthread.h”
#include <ctype.h>

typedef struct arg_set
{
char *fname;
int count;
int tid;
}ARG_SET;

bool bWait = false;
ARG_SET *mailbox = NULL;
pthread_mutex_t read_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t write_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t read_cond = PTHREAD_COND_INITIALIZER;
pthread_cond_t write_cond = PTHREAD_COND_INITIALIZER;

void main(int ac, char *av[])
{
pthread_win32_process_attach_np();
pthread_win32_thread_attach_np();

pthread_t t1, t2, t3;
ARG_SET args1, args2, args3;
void *count_words(void *);
int reports_in = 0;
int total_words = 0;

if (4 != ac)
{
    printf("usage: %s file1 file2 file3\n", av[0]);
    return;
}

args1.fname = av[1];
args1.count = 0;
args1.tid = 1;
pthread_create(&t1, NULL, count_words, (void *)&args1);

args2.fname = av[2];
args2.count = 0;
args2.tid = 2;
pthread_create(&t2, NULL, count_words, (void *)&args2);

args3.fname = av[3];
args3.count = 0;
args3.tid = 3;
pthread_create(&t3, NULL, count_words, (void *)&args3);

while(reports_in < 3)
{
    printf("MAIN: waiting for flag to go up\n");
    pthread_mutex_lock(&read_lock);
    bWait = true;
    pthread_cond_wait(&read_cond, &read_lock);
    bWait = false;
    pthread_mutex_unlock(&read_lock);
    printf("MAIN: wow! flag was raised, I have the lock\n");

    printf("MAIN: %7d: %s\n", mailbox->count, mailbox->fname);
    total_words += mailbox->count;

    Sleep(10);

    printf("MAIN: Ok, I've read the thread %d mail\n", mailbox->tid);
    
    pthread_mutex_lock(&write_lock);
    pthread_cond_signal(&write_cond);
    pthread_mutex_unlock(&write_lock);
    printf("MAIN: raising write flag\n");

    reports_in++;
}
printf("%7d: total words\n", total_words);

pthread_mutex_destroy(&read_lock);
pthread_mutex_destroy(&write_lock);
pthread_cond_destroy(&read_cond);
pthread_cond_destroy(&write_cond);

pthread_win32_thread_detach_np();
pthread_win32_process_detach_np();

system("pause");

}

void *count_words(void *a)
{
ARG_SET args = (ARG_SET)a;
FILE *fp;
int c, prevc = ‘\0’;

if (NULL != (fp = fopen(args->fname, "r")))
{
    while((c = getc(fp)) != EOF)
    {
        if (!isalnum(c) && isalnum(prevc))
        {
            args->count++;
        }
        prevc = c;
    }
    fclose(fp);
}
else
{
    perror(args->fname);
}

printf("COUNT %d: waiting to get lock\n", args->tid);    


pthread_mutex_lock(&write_lock);  
if (NULL != mailbox)
{
    printf("COUNT %d: oops..mailbox not empty. wait for signal\n", args->tid);
    pthread_cond_wait(&write_cond, &write_lock);
}

printf("COUNT %d: OK, I can write mail\n", args->tid);
mailbox = args;  


while (!bWait)
{
    Sleep(1);        
}
pthread_mutex_lock(&read_lock); 
pthread_cond_signal(&read_cond);    /* raise the flag */
pthread_mutex_unlock(&read_lock); 

printf("COUNT %d: raising read flag\n", args->tid);


pthread_mutex_unlock(&write_lock);    /* release the mailbox */
printf("COUNT %d: unlocking box\n", args->tid);    

return NULL;

}
结果:

#include <stdio.h>
#include <Windows.h>
#include “pthread.h”
#include “semaphore.h”
#include <ctype.h>

typedef struct arg_set
{
char *fname;
int count;
int tid;
}ARG_SET;

ARG_SET *mailbox = NULL;
static sem_t sem_write;
static sem_t sem_read;

void main(int ac, char *av[])
{
pthread_win32_process_attach_np();
pthread_win32_thread_attach_np();

 pthread_t t1, t2, t3;
 ARG_SET args1, args2, args3;
 void *count_words(void *);
 int reports_in = 0;
 int total_words = 0;

 if (4 != ac)
 {
     printf("usage: %s file1 file2 file3\n", av[0]);
     return;
 }

 if (-1 == sem_init(&sem_read, 0 , 1)
     || -1 == sem_init(&sem_write, 0, 0))
 {
     return;
 }

 args1.fname = av[1];
 args1.count = 0;
 args1.tid = 1;
 pthread_create(&t1, NULL, count_words, (void *) &args1);
 
 args2.fname = av[2];
 args2.count = 0;
 args2.tid = 2;
 pthread_create(&t2, NULL, count_words, (void *) &args2);
 
 args3.fname = av[3];
 args3.count = 0;
 args3.tid = 3;
 pthread_create(&t3, NULL, count_words, (void *) &args3);


 while(reports_in < 3)
 {
     printf("MAIN: waiting for sub thread write\n");
     sem_wait(&sem_write);

     printf("MAIN: %7d: %s\n", mailbox->count, mailbox->fname);
     total_words += mailbox->count;
     if ( mailbox == &args1) 
         pthread_join(t1,NULL);
     if ( mailbox == &args2) 
         pthread_join(t2,NULL);
     if ( mailbox == &args3) 
         pthread_join(t3,NULL);
     
     mailbox = NULL;
     printf("MAIN: Ok,I have read the mail\n");
     sem_post(&sem_read);  
     reports_in++;
 }


 printf("%7d: total words\n", total_words);
 sem_destroy(&sem_read);
 sem_destroy(&sem_write);

 pthread_win32_thread_detach_np();
 pthread_win32_process_detach_np();

 system("pause");

}

void *count_words(void *a)
{
struct arg_set *args = (arg_set )a; / cast arg back to correct type */
FILE *fp;
int c, prevc = ‘\0’;

 if ( (fp = fopen(args->fname, "r")) != NULL )
 {
      while( ( c = getc(fp)) != EOF )
      {
            if ( !isalnum(c) && isalnum(prevc) )
            {
                 args->count++;
            }
            prevc = c;
      }
      fclose(fp);
  } else 
       perror(args->fname);
  printf("COUNT %d: waiting for main thread read the mail\n", args->tid);
  sem_wait(&sem_read);
  printf("COUNT %d:OK,I can write mail\n", args->tid);
  mailbox = args;            /* put ptr to our args there */
  printf("COUNT %d: Finished writting\n", args->tid);
  sem_post(&sem_write);
  return NULL;

}

猜你喜欢

转载自blog.csdn.net/weixin_41438466/article/details/88561690