[20190730]Linux下的多线程编程学习笔记

参考文章: https://www.cnblogs.com/zengzy/p/5160175.html

简单的多线程编程

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void *thread(void *threadid)
{
int tid;
tid = (int)threadid;
printf("Hello World! It's me, thread #%d!/n", tid);
pthread_exit(NULL);
}
int main(void)
{
    pthread_t id;
    void *ret;
    int i,retv;
    int t=123;
    retv=pthread_create(&id,NULL,(void *) thread,(void *)t);
    if (retv!=0)
    {
        printf ("Create pthread error!/n");
        return 1;
    }
    for (i=0;i<3;i++)
        printf("This is the main process./n");
    pthread_join(id,&ret);
    printf("The thread return value is%d/n",(int)ret);
    return 0;
}
  • pthread_t

    线程标识符

  • retv=pthread_create(&id,NULL,(void *) thread,(void *)t);

    ​ pthread_create( [指向标识符的指针],[线程参数],[线程运行函数的起始地址], [参数] )

  • pthread_join(id,&ret);

    ​ pthread_join([指向标识符的指针], [被等待线程的返回值])

    这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。

  • pthread_exit(NULL);

    • 唯一的参数是函数的返回代码,只要pthread_join中的第二个参数thread_return不是NULL,这个值将被传递给 thread_return
    • 一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线程则返回错误代码ESRCH

修改线程的属性

  • 线程的属性结构:pthread_attr_t

  • 初始化函数pthread_attr_init (&attr) 均设为默认值

    • 这个函数必须在pthread_create函数之前调用
  • 属性对象主要包括(后面的项为默认值)

    • 是否绑定-非绑定
    • 是否分离-非分离
    • 堆栈地址-缺省
    • 堆栈大小-1M
    • 优先级-与父进程同样级别的优先级

绑定

  • 轻进程(LWP:Light Weight Process)

    轻进程可以理解为内核线程,它位于用户层和系统层之间。系统对线程资源的分配、对线程的控制是通过轻进程来实现的,一个轻进程可以控制一个或多个线程。

  • 默认状况下,启动多少轻进程、哪些轻进程来控制哪些线程是由系统来控制的,这种状况即称为非绑定的

  • 绑定状况下,则顾名思义,即某个线程固定的"绑"在一个轻进程之上。被绑定的线程具有较高的响应速度,这是因为CPU时间片的调度是面向轻进程的,绑定的线程可以保证在需要的时候它总有一个轻进程可用。通过设置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足诸如实时反应之类的要求。

  • pthread_attr_setscope

    • pthread_attr_setscope([指向属性结构的指针],[绑定类型])
      • 绑定类型 :PTHREAD_SCOPE_SYSTEM (绑定的)PTHREAD_SCOPE_PROCESS(非绑定的)

分离

  • 线程的分离状态决定一个线程以什么样的方式来终止自己

  • 非分离状态下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源

  • 分离状态下,线程没有被其他线程所等待,运行结束时,自己运行结束时,线程终止。

  • pthread_attr_setdetachstate(pthread_attr_t \*attr, int detachstate)

    第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)

  • **如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在 pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用 pthread_cond_timewait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。 **

优先级

  • 它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数 pthread_attr_setschedparam进行存放

线程的数据处理

和进程相比,线程的最大优点之一是数据的共享性,各个进程共享父进程处沿袭的数据段,可以方便的获得、修改数据。

在进程中共享的变量必须用关键字volatile来定义

线程数据
  • 在线程内部,各个函数可以象使用全局变量一样调用它,但它对线程外部的其它线程是不可见的。

  • 我们为每个线程数据创建一个键,它和这个键相关联,在各个线程里,都使用这个键来指代线程数据,但在不同的线程里,这个键代表的数据是不同的,在同一个线程里,它代表同样的数据内容。

创建一个键

extern int pthread_key_create __P ((pthread_key_t *__key,void (*__destr_function) (void *)));

pthread_keycreate([指向键值的指针], [析构函数]);

为一个键指定线程数据

extern int pthread_setspecific __P ((pthread_key_t __key,__const void *__pointer));

pthread_setpecific ( [指向键值的指针], [数据指针]);

从一个键读取线程数据

extern void *pthread_getspecific __P ((pthread_key_t __key));

pthread_getpecific([指向键值的指针]);

删除键

pthread_key_delete([指向键值的指针]);

  • 它只释放键占用的内存,并不释放该键关联的线程数据所占用的内存资源,而且它也不会触发函数pthread_key_create中定义的destructor函数。线程数据的释放必须在释放键之前完成。

互斥锁

互斥锁用来保证一段时间内只有一个线程在执行一段代码。

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void reader_function ( void );
void writer_function ( void );
int buffer_has_item=0;
//声明互斥锁变量
pthread_mutex_t mutex;
int main ( void )
{
  pthread_t reader;
    //初始化mutex,NULL表示默认属性
  pthread_mutex_init (&mutex,NULL);
  pthread_create(&reader, NULL, (void *)&reader_function, NULL);
  writer_function( );
  return 0;
}
void writer_function (void)
{
  while (1)
  {
    pthread_mutex_lock (&mutex);
    if (buffer_has_item==0)
    {
      buffer_has_item=1;
      printf("Write once!/n");
    }
    pthread_mutex_unlock(&mutex);
  }
}
void reader_function(void)
{
  while (1)
  {
    pthread_mutex_lock(&mutex);
    if (buffer_has_item==1)
    {
      buffer_has_item=0;
      printf("Read once!/n");
    }
    pthread_mutex_unlock(&mutex);
  }
}
  • 发生只能用于"锁"住临界代码区域
  • 一个线程加的锁必须由该线程解锁.
条件变量

使用场合:在涉及判断共同变量状态时

pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
pthread_mutex_lock(&mutex);
while([false])
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);

其中pthread_cond_wait实际上可以看作是以下几个动作的合体:

  • 解锁线程锁
  • 等待条件为true
  • 加锁线程锁.
信号量

计数器

  • 数据类型:sem_t 本质是长整型

  • 初始化:sem_init([信号量结构指针],(unsigned int)[初始值])

  • 例程

    #include <stdio.h>
    #include <string.h>
    #include <pthread.h>
    #include <errno.h>
    #include <semaphore.h>
    #define BUFSIZE 4
    #define NUMBER 8
    int sum_of_number=0;
    /* 可读 和 可写资源数*/
    sem_t write_res_number;
    sem_t read_res_number;
    /* 循环队列 */
    struct recycle_buffer{
      int buffer[BUFSIZE];
      int head,tail;
    }re_buf;
    /* 用于实现临界区的互斥锁,我们对其初始化*/
    pthread_mutex_t buffer_mutex=PTHREAD_MUTEX_INITIALIZER;
    static void *producer(void * arg)
    {
      int i;
      for(i=0;i<=NUMBER;i++)
      {
        /* 减少可写的资源数 */
        sem_wait(&write_res_number);
        /* 进入互斥区 */
        pthread_mutex_lock(&buffer_mutex);
        /*将数据复制到缓冲区的尾部*/
        re_buf.buffer[re_buf.tail]=i;
        re_buf.tail=(re_buf.tail+1)%BUFSIZE;
        printf("procuder %d write %d./n",(int)pthread_self(),i);
        /*离开互斥区*/
        pthread_mutex_unlock(&buffer_mutex);
        /*增加可读资源数*/
        sem_post(&read_res_number);
      }
      /* 线程终止,如果有线程等待它们结束,则把NULL作为等待其结果的返回值*/
      return NULL;
    }
    static void * consumer(void * arg)
    {
      int i,num;
      for(i=0;i<=NUMBER;i++)
      {
        /* 减少可读资源数 */
        sem_wait(&read_res_number);
        /* 进入互斥区*/
        pthread_mutex_lock(&buffer_mutex);
        /* 从缓冲区的头部获取数据*/
        num = re_buf.buffer[re_buf.head];
        re_buf.head = (re_buf.head+1)%BUFSIZE;
        printf("consumer %d read %d./n",pthread_self(),num);
        /* 离开互斥区*/
        pthread_mutex_unlock(&buffer_mutex);
        sum_of_number+=num;
        /* 增加客写资源数*/
        sem_post(&write_res_number);
      } 
      /* 线程终止,如果有线程等待它们结束,则把NULL作为等待其结果的返回值*/
      return NULL;
    }
    int main(int argc,char ** argv)
    {
      /* 用于保存线程的线程号 */
      pthread_t p_tid;
      pthread_t c_tid;
      int i;
      re_buf.head=0;
      re_buf.tail=0;
      for(i=0;i<BUFSIZE;i++)
        re_buf.buffer[i] =0;
      /* 初始化可写资源数为循环队列的单元数 */
      sem_init(&write_res_number,0,BUFSIZE); // 这里限定了可写的bufsize,当写线程写满buf时,会阻塞,等待读线程读取
      /* 初始化可读资源数为0 */
      sem_init(&read_res_number,0,0);
      /* 创建两个线程,线程函数分别是 producer 和 consumer */
      /* 这两个线程将使用系统的缺省的线程设置,如线程的堆栈大小、线程调度策略和相应的优先级等等*/
      pthread_create(&p_tid,NULL,producer,NULL);
      pthread_create(&c_tid,NULL,consumer,NULL);
      /*等待两个线程完成退出*/
      pthread_join(p_tid,NULL);
      pthread_join(c_tid,NULL);
      printf("The sum of number is %d/n",sum_of_number);
    }
    
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值