Linux线程介绍

一、线程的概念

1. 为什么引入线程

首先,每个进程都拥有自己的数据段、代码段和堆栈段,进程的体积庞大,在进程切换的时候操作系统开销较大,需要不断刷新高速缓存(Cache)。
为了能进一步减少处理器的空转时间和资源消耗,操作系统引入了线程这一概念,也称轻量级进程。它是进程内独立的一条运行路线,是内核调度的最小单位。

2. 线程的特点

在同一个进程中创建的所有线程共享该进程的地址空间,Linux里同样用task_struct来描述一个线程,线程和进程都参与统一的调度。

一个进程中的多个线程共享以下资源:

  • 可执行的指令
  • 静态数据
  • 文件描述符表
  • 用户ID、用户组ID
  • 信号处理函数

每个进程私有的资源为:

  • 线程ID(TID)
  • PC程序计数器
  • 堆栈
  • 执行状态、属性和优先级等

二、线程相关接口函数

在linux用户空间中线程的操作函数通常使用pthread线程库,是一个遵循POSIX标准的通用的线程库,具有良好的可移植性。
链接这些线程函数库时要使用编译器命令的“-lpthread”选项

(1)创建线程

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

参数:thread :线程对象,一个线程对应一个线程对象,也称线程标识符
attr :线程属性,默认属性填NULL,表示结合属性
start_routine :线程处理函数
//线程处理函数最多传一个参数,可不传。如果要接收多种数据,用一个结构体表示,并用结构体指针作为参数传入
arg : 给线程处理函数传参,如果线程处理函数没有参数,则填NULL

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

注:线程创建之后,就开始执行相应的线程处理函数,并在该函数运行完之后,该线程结束。使用ps -aL 命令查看轻量级进程。

(2)退出线程

#include <pthread.h>
void pthread_exit(void *retval);

参数:retval :线程结束信息,由pthread_join等待接收,如果不想接收信息则填NULL。

注:

  • 在线程中使用pthread_exit函数是主动退出线程。但不要在线程中使用exit函数进行退出,exit是让当前进程终止,在线程中使用会导致该进程中所有线程都结束。
  • 进程又称主线程(main thread),在主线程中使用pthread_exit函数不会退出整个进程,但是会让主线程结束,导致子线程还在,内存无法被回收,成为僵尸进程。为防止出现主线程先于子线程结束的情况,引入的等待线程即pthread_join函数。
  • 已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
  • 创建新的线程不会复用刚才退出线程的地址空间。

(3)等待线程

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
//等待线程一般在主线程中调用,且为阻塞函数

参数:
thread : 线程对象
retval :线程结束信息
返回值:成功则返回0,失败返回错误编号

线程使用例子:

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

typedef struct data
{
   char name[32];
   int age;
}D;//用于测试线程处理函数的传参
void *func1(void *arg);
void *func2();
int main(int argc, char *argv[])
{ 
   pthread_t thread1;
   pthread_t thread2;
   void * result1;
   void * result2;
   D stu={"xm",10};
   int ret=pthread_create(&thread1,NULL,func1,&stu);
   if(ret<0)
   {
       perror("pthread_create1");
       exit(-1);
   }
   ret=pthread_create(&thread2,NULL,func2,NULL);
   if(ret<0)
   {
       perror("pthread_create1");
       exit(-1);
   }

   int n=5;
   while(n--)
   {
      /* if(n==3)
       {
           pthread_exit(NULL);//退主线程只是结束主线程的后续执行代码,但进程空间仍然存在,子线程会继续运行
       }
       */
       printf("main thread \n");
       sleep(1);
   }
   pthread_join(thread1,&result1);
   printf("%s,阻塞结束\n",(char *)result1);
   puts("===========");
   pthread_join(thread2,&result2);
   printf("%s,阻塞结束\n",(char *)result2);
   return 0;
} 
void *func1(void *arg)
{

   D *q=(D*)arg;
   int n=3;
   while((q->age)--)
   {
      if(q->age==7)
       {
           pthread_exit("thread1 end");
       }
   
       printf("child thread1 \n");
       sleep(1);
   }
   return NULL;
}
void *func2()
{
   int n=7;
   while(n--)
   {
       printf("cccccc\n");
       sleep(1);
   }
   pthread_exit("thread2 end-----");
   return NULL;
}

三、线程间的同步与互斥

1.同步

定义:多个线程之间按照事先约定好的顺序有先后地完成某个事件。

信号量:为系统中一种资源,本质上是一个非负整数。信号量的值等于资源个数。

操作信号量只能由特定函数接口才能访问:

1)信号量的初始化 ——sem_init();

#include <semaphore.h>

int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
    sem :信号量对象
    pshared : 用于线程间同步,填0
    value : 信号量初始值
返回值:成功返回0,失败返回-1

2)P操作(申请资源)——sem_wait();

函数结构:
if(是否有资源)
{
执行后续代码;
信号量-1;
}
else
{
阻塞等待,直到有资源唤醒为止
}

#include <semaphore.h>
int sem_wait(sem_t *sem);
参数:
    sem : 信号量对象
返回值:

3)V操作(释放资源)——sem_post();

函数结构:
if(没有线程在等待该资源)
{
信号量+1;
}
else
{
唤醒第一个等待的线程,让其继续运行
}

#include <semaphore.h>
int sem_post(sem_t *sem);
参数:
    sem : 信号量对象
返回值:成功则返回0,失败返回-1且信号量值保持不变

2.互斥

当一个线程使用公共数据时,其他线程都不能访问该公共数据。

临界资源:多个线程能够访问的数据

临界区:涉及到临界资源的代码模块

互斥是使用互斥锁保护临界资源(区)。线程必须先获得互斥锁才能访问临界资源,访问完资源后释放该锁。如果无法获得锁,线程会阻塞直到获得锁为止。

1)创建互斥锁

int pthread_mutex_init(pthread_mutex_t *mutex,pthread_mutexattr_t *attr)
参数:
mutex : 锁对象
attr :互斥锁属性,默认填NULL表示缺省属性
返回值:成功返回0,失败返回-1;

2)申请锁

int pthread_mutex_lock(pthread_mutex_t *mutex)
参数:
mutex : 锁对象
返回值:成功返回0,失败返回-1;

3)释放锁

int pthread_mutex_unlock(pthread_mutex_t *mutex)
参数:
mutex : 锁对象
返回值:成功返回0,失败返回-1;

互斥锁使用例子:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <semaphore.h>
void *fun_gets();
void *fun_puts();
pthread_mutex_t mutex;
int num1=0,num2=0;
int count=0;

int main(int argc, char *argv[])
{ 
    pthread_mutex_init(&mutex,NULL);
    pthread_t thread1,thread2;
    void *ret_th1,*ret_th2;
    int ret;
    ret=pthread_create(&thread1,NULL,fun_gets,NULL);
    if(ret<0)
    {
        perror("pthread_create1");
        return -1;
    }
    ret=pthread_create(&thread2,NULL,fun_puts,NULL);
    if(ret<0)
    {
        perror("pthread_create2");
        return -1;
    }

    pthread_join(thread1,&ret_th1); 
    printf("%s\n",(char *)ret_th1);
    pthread_join(thread2,&ret_th2); 
    printf("%s\n",(char *)ret_th2);
    return 0;
} 
void *fun_gets()
{
    while(1)
    {
        pthread_mutex_lock(&mutex);
        num1=count;
        num2=count;
        count++;
        pthread_mutex_unlock(&mutex);
    }
    pthread_exit("thread1 end");
    return NULL;
}
void *fun_puts()
{
    while(1)
    {
        pthread_mutex_lock(&mutex);
        if(num1!=num2)
        {
            printf("num1=%d num2=%d\n",num1,num2);
        }
        pthread_mutex_unlock(&mutex);
    }
    pthread_exit("thread2 end");
    return NULL;
}

该程序运行时,thread1和thread2线程访问理解资源数据num1、num2和count。如果不对线程内的临界区上互斥锁,就会导致num1和num2出现值不相等情况,比如刚执行num1=count完后时间片结束,轮到thread2线程得到调用,就会出现num1 != num2的情况。

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值