浅谈一下linux线程

Posted sucfrperperseverance

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈一下linux线程相关的知识,希望对你有一定的参考价值。

  1.线程是进程中最小执行单元,多线程共享同一个进程的地址空间
  2.Linux 内核调度的对象是线程,所以一个进程中多个线程参与操作操作系统统一调度

  使用线程优点:
    <1>效率高
    <2>线程之间通信比较简单(全局变量)

使用线程缺点:
  安全性差

线程API

  1.线程创建
    int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
    void *(*start_routine) (void *), void *arg);

    参数:
      @thread 获取线程ID
      @attr 设置线程属性 NULL:默认属性
      @start_routine 线程执行的函数
      @arg 给线程执行函数传递的参数

返回值:
  成功返回0, 失败返回错误码

实例如下:

#include <head.h>

void *output(void *arg)
{
    int data = *(int *)arg;
    printf("%d : %d\\n",pthread_self(),data);
    return;
}

int main(int argc, const char *argv[])
{
    int ret;
    int count = 0;
    int data = 0;
    pthread_t tid;

    while(1)
    {
        /*bug :
         * 一个线程被创建后,不一定立即运行,而每个线程都是从data的地址中读取数据
         *,而data地址的数据又一直在发生变化,这样可能会导致,线程运行的时候,从data地址读取
         *的数据已经不是它想要的数据了。
         *
         */
        ret = pthread_create(&tid,NULL,output,&data);
        if(ret != 0){
            fprintf(stderr,"Fail to pthread_create : %s\\n",strerror(ret));
            exit(EXIT_FAILURE);
        }
        i ++;

        pthread_detach(tid);
        data ++;
        count ++;
        printf("count = %d!\\n",count);
    //    pthread_join(tid,NULL);
    }
    
    return 0;
}

运行如下

 

实例如下
创建两个子线程,子1线程输出data值,子2线程data ++操作,主线程将data -- 操作 (data 在主线程定义)

#include <head.h>

void *output_data(void *arg)
{
    int data = *(int *)arg;
    
    while(1)
    {
        data = *(int *)arg;
        printf("data = %d!\\n",data);
//        sleep(1);    
    }
}

void *add_data(void *arg)
{
    int *pdata = (int *)arg;
    while(1)
    {
        (*pdata) ++;
    }
}

int main(int argc, const char *argv[])
{
    int ret;
    pthread_t tid1,tid2;
    int data = 10;

    ret = pthread_create(&tid1,NULL,output_data,&data);
    if(ret != 0){
        fprintf(stderr,"Fail to pthread_create : %s\\n",strerror(ret));
        exit(EXIT_FAILURE);
    }
    
    ret = pthread_create(&tid2,NULL,add_data,&data);
    if(ret != 0){
        fprintf(stderr,"Fail to pthread_create : %s\\n",strerror(ret));
        exit(EXIT_FAILURE);
    }

    while(1)
    {
        data --;
    }
    
    exit(EXIT_SUCCESS);
}

 

 运行结果

可见线程的运行并没有先后顺序

2.线程退出

(1)线程函数返回 (return)
(2)pthread_exit
(3)pthread_cancel
(4)进程结束,这个进程中所有的线程都退出

void pthread_exit(void *retval);
功能:用来退出一个线程
参数:
@retval 返回一个地址值


int pthread_join(pthread_t thread, void **retval);
功能:等待一个线程退出,将退出线程未释放的资源释放掉
参数:
@thread 需要等待退出线程的ID
@retval 获得线程退出返回的值


void *thread_function(void *arg)
{
int data = 100;

pthread_exit(&data);
}

int main()
{
int *p;

pthread_join(tid,&p);
return 0;
}

3.将线程标记分离

分离状态的线程在结束的时候,系统自动回收它的资源

int pthread_detach(pthread_t thread);
@thread 线程ID


实例如下:
  (1)全局int data[] = {1,2,3,4,5,6,7,8,9,10,11};
  (2)线程1一直对全局数组做逆置
  (3)线程2一直对全局数组输出

 

#include <head.h>

//pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock;

int a[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};

void *output_array(void *arg)
{
    int i = 0;
    
    //实现效果:如果发现逆置操作没有结束,则不输出此时阻塞自己
    while(1)
    {    
        pthread_mutex_lock(&lock);
        for(i = 0;i < 20;i ++){
            printf("%d ",a[i]);
        }
        printf("\\n");
        pthread_mutex_unlock(&lock);
    
    }
}

void *reverse_array(void *arg)
{
    int t;
    int i ,j;
    
    //实现效果:如果输出操作没有结束,则不逆置阻塞字节
    while(1)
    {
        pthread_mutex_lock(&lock);
        i = 0;
        j = 19;

        while(i < j){
            t = a[i];
            a[i] = a[j];
            a[j] = t;

            i ++;
            j --;
        }
        pthread_mutex_unlock(&lock);
    }
}

int main(int argc, const char *argv[])
{
    int ret;
    pthread_t tid1,tid2;
    int data = 10;

    ret = pthread_mutex_init(&lock,NULL);
    if(ret != 0){
        fprintf(stderr,"Fail to pthread_mutex_init");
        exit(EXIT_FAILURE);
    }

    ret = pthread_create(&tid1,NULL,output_array,NULL);
    if(ret != 0){
        fprintf(stderr,"Fail to pthread_create : %s\\n",strerror(ret));
        exit(EXIT_FAILURE);
    }
    
    ret = pthread_create(&tid2,NULL,reverse_array,NULL);
    if(ret != 0){
        fprintf(stderr,"Fail to pthread_create : %s\\n",strerror(ret));
        exit(EXIT_FAILURE);
    }

    pthread_join(tid1,NULL);
    
    exit(EXIT_SUCCESS);
}

 

运行结果

并没有出现还没有逆置完成就输出的

但如果去掉

pthread_mutex_lock(&lock);

pthread_mutex_unlock(&lock);

呢?

 线程互斥锁

  功能:对共享资源实现互斥访问,保证访问的完整性

  如果使用互斥锁,每个线程在访问共享资源,都必须先获得互斥锁,然后在访问共享资源,如果无法获得互斥锁
  ,则表示有其他线程正在访问共享资源,此时没有获得锁的线程会阻塞,直到其他线程释放锁,能再次获得锁

1.定义互斥锁[全局,让每个线程都可以访问到]
pthread_mutex_t lock;

2.初始化互斥锁

//[线程创建之前]动态初始化,属性使用默认 NULL
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);

//静态初始化定义初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

3.获得互斥锁
int pthread_mutex_lock(pthread_mutex_t *mutex);//操作共享资源之前加锁
int pthread_mutex_trylock(pthread_mutex_t *mutex);

4.释放锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);//操作共享资源结束的时候

5.销毁锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);//不需要再次使用锁的时候

 线程间同步

  同步:相互之间配合完成一件事情
  互斥:保证访问共享资源的完整性(有你没我)

  POSIX 线程中同步:使用信号量实现

  信号量 : 表示一类资源,它的值表示资源的个数

对资源访问:
  p操作(申请资源) [将资源的值 - 1]
  ....
  V操作(释放资源) [将资源的值 + 1]

1.定义信号量
sem_t sem ;

2.初始化信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
  @sem 信号量
  @pshared 0:线程间使用
  @value 初始化的信号量的值
返回值:
  成功返回0,失败返回-1

3.P操作
  int sem_wait(sem_t *sem);

4.V操作
  int sem_post(sem_t *sem);

实例如下:
创建两个线程
write_thread : 从键盘上读取用户输入的数据,写到文件中
read_thread : 从文件中读取数据,然后输出
main_thread : 打开一个文件

#include <head.h>

sem_t read_sem;//读资源
sem_t write_sem;//写资源


void *write_thread(void *arg)
{
    char buf[1024];
    int fd = *(int *)arg;

    while(1){
        //申请写资源
        if(sem_wait(&write_sem) < 0){
            perror("Fail to sem_wait");
            pthread_exit(NULL);
        }

        fgets(buf,sizeof(buf),stdin);
        buf[strlen(buf) - 1] = \'\\0\';

        write(fd,buf,strlen(buf));

        //还原偏移量到上一次值
        lseek(fd,-strlen(buf),SEEK_CUR);

        //释放读资源
        if(sem_post(&read_sem) < 0){
            perror("Fail to sem_wait");
            pthread_exit(NULL);
        }

        if(strcmp(buf,"quit") == 0){
            break;
        }

    }

    pthread_exit(NULL);
}

void *read_thread(void *arg)
{
    int n;
    char buf[1024];
    int fd = *(int *)arg;

    while(1){
        //申请读资源
        if(sem_wait(&read_sem) < 0){
            perror("Fail to sem_wait");
            pthread_exit(NULL);
        }

        n = read(fd,buf,sizeof(buf));
        buf[n] = \'\\0\';
        printf("Read %d bytes : %s!\\n",n,buf);

        //释放写资源
        if(sem_post(&write_sem) < 0){
            perror("Fail to sem_wait");
            pthread_exit(NULL);
        }

        if(strcmp(buf,"quit") == 0){
            break;
        }
    }

    pthread_exit(NULL);
}

//./a.out  file
int main(int argc, const char *argv[])
{
    int fd;
    int ret;
    pthread_t rtid;
    pthread_t wtid;

    if(argc < 2){
        fprintf(stderr,"Usage : %s <filename>!\\n",argv[0]);
        exit(EXIT_FAILURE);
    }

    fd = open(argv[1],O_RDWR | O_CREAT | O_TRUNC,0666);
    if(fd < 0){
        fprintf(stderr,"Fail to open %s : %s\\n",argv[1],strerror(errno));
        exit(EXIT_FAILURE);
    }

    if(sem_init(&read_sem,0,0) < 0){
        perror("Fail to sem_init");
        exit(EXIT_FAILURE);
    }

    if(sem_init(&write_sem,0,1) < 0){
        perror("Fail to sem_init");
        exit(EXIT_FAILURE);
    }

    ret = pthread_create(&rtid,NULL,read_thread,&fd);
    if(ret != 0){
        fprintf(stderr,"Fail to pthread_create : %s!\\n",strerror(ret));
        exit(EXIT_FAILURE);
    }

    ret = pthread_create(&wtid,NULL,write_thread,&fd);
    if(ret != 0){
        fprintf(stderr,"Fail to pthread_create : %s!\\n",strerror(ret));
        exit(EXIT_FAILURE);
    }
    
    pthread_join(rtid,NULL);
    pthread_join(wtid,NULL);

    return 0;
}

 

以上是关于浅谈一下linux线程的主要内容,如果未能解决你的问题,请参考以下文章

浅谈linux下进程最大数最大线程数进程打开的文件数

浅谈 Linux 的死锁检测

(转)浅谈 Linux 内核无线子系统

线程学习知识点总结

线程上的Android片段替换(...)

浅谈多线程