线程控制知识点总结(下)

私有数据:

在多线程环境下,进程内的所有线程共享进程的数据空间,因此全局变量为所有线程共有。
而有时候我们需要保存线程自己的全局变量,这种特殊的变量仅在某个线程内部有效。
可以通过创建线程的私有数据来解决:在线程内部,私有数据可以被各个函数访问,但对其他线程是屏蔽的。
线程的私有数据采用了—— 一键多值 的技术,即一个键对应多个数值,访问数据时通过键值来访问,好像是对一个变量进行访问,其实是在访问不同的数据,
使用线程私有数据时,首先要为每个线程数据创建一个相关联的键,在各个线程内部,都使用这个公用的键来指代线程数据。但在不同的线程中,这个键所代表的数据是不同的。

  • pthread_key_create ( pthread_key_t key ,void ( *destr_function )( void ) ) :

    从linux 的TSD池中分配一项,将其值赋给key供以后访问使用,第一个参数为指向键值的指针,第二个参数为一个函数指针。key一旦被创建,所有线程都可以访问它,但各线程可根据自己的需要往key中填入不同的值。

  • pthread_setspecific ( pthread_key_t key,const void *pointer )

    该函数将pointer的值(不是内容)与key相关联,用pthread_setspecific为一个键指定新的线程数据时,线程必须先释放原有的线程数据用以回收空间。

  • pthread_getspecific ( pthread_key_t key ):

    通过该函数得到一个与key相关联的数据

  • pthread_key_delete ( pthread_key_t key) :

    该函数用来删除一个键,删除后,键所占用的内存将被释放,需要注意的是,键占用的内存被释放,但是与键关联的线程数据所占用的内存并不被释放。因此,线程数据的释放必须在释放键之前完成。

代码:

#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
pthread_key_t key;
void * thread2(void *arg)
{
  int tsd =5;
  printf("线程2 %d 正在运行\n",pthread_self());
  pthread_setspecific(key,(void *)tsd);       //为线程2分配键值5
  printf("线程2 %d 的键值为:%d\n",pthread_self(),pthread_getspecific(key));
}
void * thread1(void *arg)
{
  int tsd=0;
  pthread_t thid2;
  printf("线程1 %d 正在运行\n",pthread_self());
  pthread_setspecific(key,(void *)tsd);      //为线程1分配键值
  pthread_create(&thid2,NULL,thread2,NULL);  //创建线程2.运行函数
  sleep(4);
  printf("线程1 %d 的键值为:%d\n",pthread_self(),pthread_getspecific(key));
}
int main()
{
  pthread_t thid1;
  printf("主线程正在运行:\n");
  pthread_key_create (&key,NULL);      //创建键
  pthread_create (&thid1,NULL,thread1,NULL);     //创建新线程
  sleep(8);
  pthread_key_delete(key);
  printf("主进程结束\n");
  return 0;

}
//要注意主线程和线程1之间的休眠sleep()关系,如果主线程的休眠时间结束,那么直接回到主线程

线程同步:

1.互斥锁

 互斥锁通过锁机制来实现线程间的同步。在同一时刻它通常只允许一个线程执行一个关键部分的代码

  • 初始化
    pthread_mutex_init

    初始化一个互斥锁:两种方式:
    1 .pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    2 .pthread_mutex_init ( pthread_mutex_t *mutex,const pthread_mutexattr_t *mutexattr);

  • 加锁
    pthread_mutex_lock ( pthread_mutex_t *mutex );
    pthread_mutex_trylock ( pthread_mutex_t *mutex);

    使用第一个函数加锁时,如果mutex已经被锁住,当前尝试加锁的线程就会阻塞,直到互斥锁被其他线程释放。当函数返回时,说明已经加锁成功。而第二个函数,如果mutex已经被锁住,会立即返回,返回的错误EBUSY.

  • 解锁
    pthread_mutex_unlock ( pthread_mutex_t *mutex);

    解锁函数在使用时必须满足:1.互斥锁处于加锁状态。2.调用本函数的线程必须是给互斥锁加锁的线程,解锁后若有其他线程在等待互斥锁,等待队列中的第一个线程将获得互斥锁。

  • 清除锁
    pthread_mutex_destory ( pthread_mutex_t *mutex );

    清除一个互斥锁意味着释放它所占的资源。清除锁时要求锁处于已解锁状态。若锁处于锁定状态,函数返回EBUSY,成功执行则返回0。

2.条件变量

条件变量是利用线程间共享的全局变量进行同步的一种机制。
主要包括:一个等待使用资源的线程等待“条件变量被设置为真”;另一个线程在使用完资源后“设置条件为真”。
为了保证条件变量可以正确的被修改,使用中需要用到互斥锁。

  • 初始化:
    两种方法:
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    pthread_cond_init ( pthread_cond_t * cond,pthread_condattr_t * cond_attr );

    cond_attr参数是条件变量的属性,由于其并没有得到实现,所以值通常是NULL;

  • 等待条件成立:
    pthread_cond_wait ( pthread_cond_t * cond,pthread_mutex_t *mutex );
    pthread_cond_timewait( pthread_cond_t *cond,pthread_mutex_t *mutex,const struct timespec *abstime );

    wait函数释放由mutex指向的互斥锁,同时使当前线程关于cond指向的条件变量阻塞,直到条件被信号唤醒。通常条件表达式在互斥锁的保护下求值,如果条件表达式为假,那么线程基于条件变量阻塞。当一个线程改变条件变量的值时,条件变量获得一个信号,使得等待条件变量的线程退出阻塞状态。

  • 条件变量激活:
    pthread_cond_signal ( pthread_cond_t *cond );
    pthread_cond_broadcast ( pthread_cond_t *cond );

    signal激活一个等待条件成立的线程,存在多个等待线程时,按入队顺序激活其中一个; broadcast则激活所有等待的线程。

  • 清除条件变量
    pthread_cond_destory ( pthread_cond_t * cond );

    只有在没有线程等待该条件变量的时候才能清除这个条件变量,否则返回EBUSY

代码:

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

pthread_mutex_t mutex;
pthread_cond_t cond;

void *thread1(void *arg)
{
  pthread_cleanup_push(pthread_mutex_unlock,&mutex);       //---自动释放资源函数
  while(1)
  {
    printf("线程1正在运行\n");
    pthread_mutex_lock(&mutex);      //设置线程1互斥锁
    pthread_cond_wait(&cond,&mutex);     //
    printf("线程应用条件:\n");
    pthread_mutex_unlock(&mutex);    //解锁
    sleep(4);
  }
  pthread_cleanup_pop(0);
}
void *thread2(void *arg)
{
  while(1)
  {
    printf("线程2正在运行\n");
    pthread_mutex_lock(&mutex);       //设置线程2互斥锁
    pthread_cond_wait(&cond,&mutex);   //
    printf("线程2应用条件:\n");
    pthread_mutex_unlock(&mutex);
    sleep(1);
  }
}
int main()
{
  pthread_t tid1,tid2;
  printf("条件变量学习\n");
  pthread_mutex_init(&mutex,NULL);      //初始化一个互斥锁
  pthread_cond_init(&cond,NULL);       //初始化一个条件变量
  pthread_create(&tid1,NULL,(void *)thread1,NULL);     //创建新的线程1
  pthread_create(&tid2,NULL,(void *)thread2,NULL);     //创建新的线程2
  do {
    pthread_cond_signal(&cond);
  } while(1);
  sleep(50);
  pthread_exit(0);
}

3.异步信号

线程是在内核外实现的。信号与任何线程都是异步的,也就是说信号到达线程的时间是不定的。
如果有多个线程可以接收异步信号,则只有一个被选中。如果并发的多个同样的信号被送到一个进程,每一个将被不同的线程处理。如果所有的线程都屏蔽该信号,则这些信号将被挂起,直到有信号解除屏蔽来处理他们。

出错处理

1.错误检查:

函数执行失败时,一般都会返回一个特定的值,但错误原因并没有说明。
头文件errno.h中的变量errno
程序开始执行时,变量errno被初始化为0,许多库函数在执行过程中遇到错误时就会将它设置为相应的错误码。
代码:

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
int main()
{
  FILE *fd;
  char * filename = "test";
  errno = 0;                //错误变量初始化为0
  fd =fopen(filename,"r");
  if(fd=NULL)
  {
    printf("打开文件 %s 失败,错误是 %d\n",filename,errno);
  }
  else
  {
    printf("打开文件 %s 成功\n",filename);
  }
}

2.错误的提示信息

当程序出现错误时,可以打印出相应的错误提示信息,以便用户修改错误。
函数strerror和perror可以通过错误码获取标准的错误提示信息
* streeor ( int errnum ):
函数根据参数errnum提供的错误码获取一个描述错误信息的字符串。errnum 的值通常就是 errno.
* perror ( const char *message ):
打印错误信息到屏幕或命令行终端,如果参数message是一个空指针,perror仅仅根据errno打印出对应的错误提示信息。
如果提供一个非空的值,peeor会把message加在其输出信息的前面,会添加一个冒号和空格将message和错误信息分开。
代码:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
FILE *open_file(char *filename)
{
  FILE *stream;
  errno=0;
  stream=fopen(filename,"r");
  if(stream==NULL)
  {
    printf("不能打开文件:%s\n,原因是:%s\n",filename,strerror(errno));
    exit(-1);
  }
  else
  {
    return stream;
  }
}
int main()
{
  char *filename = "test1";
  open_file(filename);
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值