IO【5】(线程)

186 阅读18分钟

​ “这是我参与8月更文挑战的第16天,活动详情查看:8月更文挑战

线程

线程的概念:线程是进程的进一步抽象。进程包括两个集合。一个资源的集合,一个线程的集合,进程是资源分配的最小单位,**线程是系统调度的最小单位。**每个进程中必然包括一个线程,那么这个线程被称作为主线程。线程其实本质是在运行一个线程函数。 线程也属于并发,也会拥有自己的资源如,pc程序计数器,时间片,堆,栈,线程号等。**线程本身并不会去申请资源 ,而是共享进程的资源。线程也被称作为轻量级的进程。线程间共享全局变量,**造成了通信时会很方便。

线程不属于标准c库函数也不属于系统调用,而是属于第三方库。libpthread.so

安装库:sudo apt-get install manpages-posix manpages-posix-dev

线程的接口函数

1.pthread_create

  1. 头文件:#include <pthread.h>

  2. 原型:(很长)

    int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine)(void *), void *arg); 
    
  3. 功能:创建一个线程

  4. 参数:

    ​ thread :线程号

    ​ attr :线程的属性和设置堆栈的大小

    ​ 分离属性:线程结束后,自己归回空间

    ​ 非分离属性:线程结束后,由创建者去归还空间

    ​ 一般填写NULL 如果想设置后续有固定的函数

    ​ start_routine:函数指针,指向了一个返回值是void * 参数是void * 的函数

    ​ arg :给线程函数传参使用,一般用于线程间通信使用,如果不进行传参 填写NULL

  5. 返回值:

    ​ 如果成功返回 0

    ​ 如果失败返回错误码 strerror()

  6. 注意:线程使用注意点

    1.在线程运行过程之中,进程结束时资源释放后,线程没有办法执行了

    2.在多线程中,线程中尽量不调用影响整个进程的函数接口,线程本身安全性不高,一个线程崩溃会造成进程的崩溃。 (下面的线程加了exit(0)后也会结束主线程)

    3.线程中通信方式2种 (全局变量)和(参数arg)(注意这个传参的方式)

    注意:在使用全局变量时,如果一个线程对其进行改变,所有线程访问的值都是改变后的值。

  7. 代码:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
//int a = 100;
void * myfun(void * arg)//万能地址
{   
    int a = *(int *)arg; //线程传参,因为没有全局变量之后通过这种方式进行传参。
    printf("我是第一个线程\n");
    printf("子线程获取A的值为%d\n",a);
    //a = 200;
    //exit(0);    //结束进程

}
int main(int argc, const char *argv[])
{
      //创建线程
      int  a  = 100;
      pthread_t tid = 0;
      if(pthread_create(&tid,NULL,myfun,&a) != 0)
      {
         printf("创建线程失败了\n");
          return -1;
      }
      printf("线程号为%ld\n",tid);
      printf("主线程获取A的值为%d\n",a);
      sleep(1);
     //printf("主线程获取A的值为%d\n",a);

    return 0;
}
linux@ubuntu:~/demo/test/IO/test$ gcc test.c -lpthread
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
线程号为139835102349056
主线程获取A的值为100
我是第一个线程
子线程获取A的值为100

注意:如果不加sleep(1),主线程结束,主进程结束之前线程会运行,但没法继续执行了

2.pthread_exit

  1. 原型:void pthread_exit(void *retval);
  2. 功能:结束一个线程
  3. 参数:retval:线程结束时的状态,如果不传递填写NULL
  4. 返回值:无
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
//int a = 100;
void * myfun(void * arg)//万能地址
{   
    int a = *(int *)arg; //线程传参,因为没有全局变量之后通过这种方式进行传参。
    printf("我是第一个线程\n");
    printf("子线程获取A的值为%d\n",a);
	pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
      //创建线程
      int  a  = 100;
      pthread_t tid = 0;
      if(pthread_create(&tid,NULL,myfun,&a) != 0)
      {
         printf("创建线程失败了\n");
          return -1;
      }
      printf("线程号为%ld\n",tid);
      printf("主线程获取A的值为%d\n",a);
      sleep(1);
      printf("主线程获取A的值为%d\n",a);

    return 0;
}

linux@ubuntu:~/demo/test/IO/test$ gcc test.c -lpthread
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
线程号为140156191627008
主线程获取A的值为100
我是第一个线程
子线程获取A的值为100//线程结束
主线程获取A的值为100//一秒后主线程继续打印,如果是exit(0)就都结束了

3.pthread_join

  1. 原型:int pthread_join(pthread_t thread, void **retval);

  2. 功能:阻塞等待回收指定线程

    等待由thread指定的线程终止。如果该线程已经终止,那么pthread_join()将立即返回。由thread指定的线程必须是可连接的。

  3. 参数:

    ​ thread:目标回收的线程号

    ​ retval :线程结束时exit返回的状态 如果不考虑填写NULL

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

  5. 代码:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
//int a = 100;
void * myfun(void * arg)
{   
    int a = *(int *)arg; //线程传参
    printf("我是第一个线程\n");
    sleep(3);//代表线程工作 3 S
    printf("子线程获取A的值为%d\n",a);
    pthread_exit(NULL);

}
int main(int argc, const char *argv[])
{
    //创建一下线程
      int  a  = 100;
      pthread_t tid = 0;
      if(pthread_create(&tid,NULL,myfun,&a) != 0)
      {
         printf("创建线程失败了\n");
          return -1;
      }
      printf("线程号为%ld\n",tid);
      printf("主线程获取A的值为%d\n",a);
      if(0 != pthread_join(tid,NULL))
      {
        printf("回收资源失败\n");
        return -1;
      }
	  printf("回收成功\n");
    return 0;
}
linux@ubuntu:~/demo/test/IO/test$ gcc test.c -lpthread
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
线程号为140436137625344
主线程获取A的值为100
我是第一个线程
子线程获取A的值为100//线程这里延迟三秒
回收成功//主线程等待

4.pthread_detach

  1. 原型: int pthread_detach(pthread_t thread);

  2. 功能:设置线程分离属性。将线程标识为已分离的线程。当一个分离的线程终止时,它的资源被自动释放回系统,而不需要另一个线程与终止的线程连接

  3. 参数:thread:目标线程号

  4. 返回值:

    ​ 成功返回 0

    ​ 失败返回错误码

    注意:如果说设置完分离属性之后,线程自动归回资源,无需join回收,join会失败

  5. 代码:

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

void * myfun(void * arg)
{   
    int a = *(int *)arg; //线程传参
    printf("我是第一个线程\n");
    sleep(3);//代表线程工作 3 S
    printf("子线程获取A的值为%d\n",a);
    pthread_exit(NULL);

}
int main(int argc, const char *argv[])
{
    //创建一下线程
    int  a  = 100;
    pthread_t tid = 0;
    if(pthread_create(&tid,NULL,myfun,&a) != 0)
    {
        printf("创建线程失败了\n");
        return -1;
    }
      //设置分离属性
    if(0!= pthread_detach(tid))
    {
        printf("设置分离属性失败\n");
        return -1;
    }
    printf("线程号为%ld\n",tid);
    printf("主线程获取A的值为%d\n",a);
    if(0 != pthread_join(tid,NULL))
    {
        printf("回收资源失败\n");
        return -1;
    }
    printf("回收成功\n");
    
    return 0;
}
linux@ubuntu:~/demo/test/IO/test$ gcc test.c -lpthread
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
线程号为140190860269312
主线程获取A的值为100
回收资源失败

5.pthread_self

  1. 头文件: #include <pthread.h>

  2. 原型:pthread_t pthread_self(void);

  3. 功能:获取自己的线程号 (返回调用线程的ID。这个值与创建该线程的pthread_create调用中的*thread(线程号)中返回的值相同。)

  4. 参数:无

  5. 返回值:

    ​ 成功会返回一个线程ID号

    ​ 没有失败

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
//int a = 100;
void * myfun(void * arg)
{   
    int a = *(int *)arg; //线程传参
	//获取ID
    printf("我是第一个线程,我的线程ID是%ld\n",pthread_self());
   
    sleep(3);//代表线程工作 3 S
    printf("子线程获取A的值为%d\n",a);
    pthread_exit(NULL);

}
int main(int argc, const char *argv[])
{
    //创建一下线程
    int  a  = 100;
    pthread_t tid = 0;
    if(pthread_create(&tid,NULL,myfun,&a) != 0)
	{
       printf("创建线程失败了\n");
        return -1;
    }
    if(0!= pthread_join(tid,NULL))
    {
      printf("回收失败\n");
      return -1;

    }
    printf("回收成功\n");
    return 0;
}

linux@ubuntu:~/demo/test/IO/test$ gcc test.c -lpthread
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
我是第一个线程,我的线程ID140150513268480
子线程获取A的值为100
//等待3s
回收成功

6.pthread_cancel

  1. 原型: int pthread_cancel(pthread_t thread);
  2. 功能:申请结束一个线程 (向线程发送一个取消请求。目标线程是否以及何时对取消请求做出反应取决于该线程控制的两个属性:状态和类型。)
  3. 参数:目标结束的线程号
  4. 返回值:成功返回0 失败非0
  5. 代码:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
//int a = 100;
void * myfun(void * arg)
{   
	sleep(1);
	int a = *(int *)arg; //线程传参
    printf("我是第一个线程,我的线程ID是%ld\n",pthread_self());
   
    sleep(3);//代表线程工作 3 S
    printf("子线程获取A的值为%d\n",a);
    pthread_exit(NULL);

}
int main(int argc, const char *argv[])
{
    //创建一下线程
    int  a  = 100;
    pthread_t tid = 0;
    if(pthread_create(&tid,NULL,myfun,&a) != 0)
    {
       	printf("创建线程失败了\n");
       	return -1;
    }
    //设置分离属性
    if(0!= pthread_detach(tid))
    {
		printf("分离失败\n");
	  	return -1;
    }
	  
    pthread_cancel(tid); //发送一个申请取消线程的信号
    printf("线程号为%ld\n",tid);
    printf("主线程获取A的值为%d\n",a);
      
    printf("回收成功\n");
    sleep(3);
    
	return 0;
}
//这里我在线程开始前延迟1秒,又可以验证线程是同时进行的
//注意这里要设置分离线程,如果使用join会阻塞,仍会打印线程的内容
linux@ubuntu:~/demo/test/IO/test$ gcc test.c -lpthread
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
线程号为140278153197312
主线程获取A的值为100
回收成功
//如果不设置线程的延迟一秒
linux@ubuntu:~/demo/test/IO/test$ gcc test.c -lpthread
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
线程号为140277385803520
主线程获取A的值为100
回收成功
我是第一个线程,我的线程ID140277385803520
//如果主线程没有延迟一秒结束
linux@ubuntu:~/demo/test/IO/test$ gcc test.c -lpthread
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
线程号为140108615382784
主线程获取A的值为100
回收成功

如果:在线程延迟一秒,在主线程分离之后在延迟两秒,线程都不会打印

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
//int a = 100;
void * myfun(void * arg)
{   
	sleep(1);
	int a = *(int *)arg; //线程传参
    printf("我是第一个线程,我的线程ID是%ld\n",pthread_self());
   
    //sleep(3);//代表线程工作 3 S
    printf("子线程获取A的值为%d\n",a);
    pthread_exit(NULL);

}
int main(int argc, const char *argv[])
{
    //创建一下线程
    int  a  = 100;
    pthread_t tid = 0;
    if(pthread_create(&tid,NULL,myfun,&a) != 0)
    {
       printf("创建线程失败了\n");
       return -1;
    }
	//设置分离属性
    if(0!= pthread_detach(tid))
    {
      printf("分离失败\n");
	  return -1;
    }
    pthread_cancel(tid); //发送一个申请取消线程的信号
    sleep(2);
	printf("线程号为%ld\n",tid);
    printf("主线程获取A的值为%d\n",a);
      
    printf("回收成功\n");
   // sleep(1);
    
	return 0;
}
linux@ubuntu:~/demo/test/IO/test$ gcc test.c -lpthread
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
线程号为140061477541632
主线程获取A的值为100
回收成功

如果延迟一秒设置分离变量

linux@ubuntu:~/demo/test/IO/test$ gcc test.c -lpthread
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
我是第一个线程,我的线程ID140124568741632
子线程获取A的值为100
线程号为140124568741632
主线程获取A的值为100
回收成功

线程间的同步机制

📝练习:创建两个线程,一个线程对全局变量count 循环自加,每一秒加一次。另一个线程循环打印count的值,每一秒打印一次。

代码:

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

int count = 0;

void *myfun(void *arg)
{
	while(1)
	{
		count++;
		sleep(1);
	}
}

int main(int argc, const char *argv[])
{
	//创建线程
	pthread_t tid;
	if(0 != pthread_create(&tid,NULL,myfun,NULL))
	{
		perror("create");
		return -1;
	}

	while(1)
	{
		printf("count = %d\n",count);
		sleep(1);
		
	}

	return 0;
}
//📣这个是没有在线程里加入延迟一秒的结果
linux@ubuntu:~/demo/test/IO/test$ gcc test.c -lpthread
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
count = 0
count = 42224582
count = 109706125
count = 187674369
count = 256430230

//延迟一秒后结果就正常了,但是这样做是有bug的,可能打印打印这就会有重复的情况。
linux@ubuntu:~/demo/test/IO/test$ gcc test.c -lpthread
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
count = 0
count = 1
count = 2
count = 3

设置一个标记flag,这样做要考虑一个问题,while(1)循环会占用很大的cpu空间,很浪费,效率不高。可以用下一节的无名信号量。

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int count = 0;//全局变量
int flag = 0;
//线程函数

void *myfun1(void *arg)  //线程函数
{
    while (1)
    {
        if(flag == 0)
        {
    	    count++;
        	sleep(1);
        	flag=1;
        }
    }
    

}

int main(int argc, const char *argv[])
{
    //开始创建线程
    pthread_t tid = 0;
    if(0 != pthread_create(&tid,NULL,myfun1,NULL))
    {
        perror("creat");
        return -1;
    }

    //主线程干自己的事情
    while(1)
    {
        if(flag == 1)
        {
    	    printf("count = %d\n",count);
        	sleep(1);
        	flag = 0;
        }
    }
    return 0;
}

同步的概念:

同步:线程间也存在着竞态的关系,对有些任务时不友好的。所以说线程提供了可以实现让多个线程同步(按一定的顺序去执行)去执行。

无名信号量:又名信号灯(用在线程之间)

本质是一个全局的特殊变量这个值不允许小于0

无名信号灯的接口

1.sem_init

  1. 头文件: #include <semaphore.h>

  2. 原型:int sem_init(sem_t *sem, int pshared, unsigned int value);

  3. 功能:初始化信号灯

  4. 参数:

    ​ sem :信号灯变量的地址

    ​ pshared :

    ​ 如果为0 代表线程间使用 (一般都用0)

    ​ 如果非0 为亲缘间进程使用

    ​ value :信号量的初值

  5. 返回值 :

    ​ 成功返回0

    ​ 失败返回 -1;

2.sem_wait

  1. 原型:int sem_wait(sem_t *sem);

  2. 功能:申请资源--又名P操作 也就是信号灯-1操作

    (递减(锁)Sem所指向的信号量。如果信号量的值大于0,则继续递减,函数立即返回。如果当前信号量的值为0,则调用会阻塞,直到可以执行递减操作(即信号量的值大于0),或者信号处理程序会中断。)

  3. 参数:sem:目标信号灯

  4. 返回值:

    ​ 成功返回0

    ​ 失败返回-1

3.sem_post

  1. 原型: int sem_post(sem_t *sem);

  2. 功能:释放资源 --又名v操作 也就是信号灯 +1操作,v操作可以多次执行,但是我们一般把信号量控制在0和1,因为这样可以实现阻塞。

    增加(解锁)指向的信号量。如果信号量的值因此大于零,那么在sem wait( )调用中被阻塞的另一个进程或线程将被唤醒并继续锁定信号量。

  3. 参数:sem :目标信号灯

  4. 返回值:

    ​ 成功返回0

    ​ 失败返回-1;

4.sem_destroy

  1. 原型:int sem_destroy(sem_t *sem);

  2. 功能:销毁一个信号灯

    ​ 只有被sem init初始化的信号量才应该使用sem destroy()销毁。

    ​ 销毁其他进程或线程当前阻塞的信号量会产生未定义的行为

    ​ 使用已经被销毁的信号量会产生未定义的结果,直到该信号量被重新初始化。

  3. 参数:sem :目标信号灯

  4. 返回值:

    ​ 成功返回0

    ​ 失败返回-1

接下来利用两个信号灯,实现一直打印输出count的值,且count一直加一:

图一:

Screenshot_20210828_152057_com.fluidtouch.noteshe.jpg

因为要先打印,所以从线程2开始的

[信号1]灯初始化为1,

[信号2]灯初始化为0(阻塞)

如图一。

接下来执行操作,这里我把主线程认为了线程2。

这是线程2做的事

📣注意:因为线程是同时开始进行的,但是线程1被堵塞在那里,所以线程2要先开始执行。

  1. 首先线程2申请信号灯1的资源([信号灯1]-1);
  2. 接着执行printf注意这里延迟一秒,这样演示效果好一点,不然直接就加到几万了;
  3. 最后执行释放信号灯2的资源([信号灯2]+1)。

然后就变成了图三那样。

图二:

Screenshot_20210828_144205_com.fluidtouch.noteshe.jpg

图三:

Screenshot_20210828_144152_com.fluidtouch.noteshe.jpg

这时候线程2阻塞,接下来是线程1做的事

  1. 首先线程1申请信号灯2的资源([信号灯2]-1);
  2. 接着执行count++;,这里要注意哦:到了这一步的时候,count++没有执行完的话,如果线程2到了会阻塞等待信号灯2的资源释放;
  3. 最后执行释放信号灯1的资源([信号灯1]+1)),然后就又变成了图二那样。

然后一直这样循环,完成+1的操作。

代码:

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

int count = 0;//全局变量

//定义无名信号灯
sem_t sem1; // 给线程2使用,初值应该为1
sem_t sem2;// 给线程1使用,初值应该为0

//线程1函数
void *myfun1(void *arg)  //线程函数
{
    while (1)
    {
        //申请资源向信号sem2
        sem_wait(&sem2);
        count++;
        sem_post(&sem1); //释放sem1 信号灯 +1      
    }
}

int main(int argc, const char *argv[])
{
    //初始化无名信号灯,分别初始化值为1 0 
    sem_init(&sem1,0,1);
    sem_init(&sem2,0,0);
    //开始创建线程
    pthread_t tid = 0;
    if(0 != pthread_create(&tid,NULL,myfun1,NULL))
    {
        perror("creat");
        return -1;
    }
    //主线程干自己的事情(线程2)
    while(1)
    {
        //申请资源对信号灯sem1
        sem_wait(&sem1);
        printf("count = %d\n",count);
        sleep(1);
        sem_post(&sem2);
    }
    return 0;
}
linux@ubuntu:~/demo/test/IO/test$ gcc test.c -lpthread
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
count = 0
count = 1
count = 2
count = 3

再理一遍思路:

第一步初始化

sem_init(&sem1,0,1);//线程2
sem_init(&sem2,0,0);//线程1

第二步设置线程1

sem_wait(&sem2);//因为sem2初始化为0,所以处于堵塞
count++;
sem_post(&sem1); //释放信号灯1的资源 +1  

第三步设置线程2

sem_wait(&sem1);//申请信号灯1资源 -1
printf("count = %d\n",count);
sleep(1);
sem_post(&sem2);//释放信号灯2的资源 +1

5.sem_trywait

  1. 原型:int sem_trywait(sem_t *sem);

  2. 功能:以非阻塞的方式去申请资源,如果申请到了就ok如果申请时没有资源会报错返回

  3. 参数:sem :目标信号灯

  4. 返回值:

    ​ 成功返回0

    ​ 失败返回-1;

6.sem_timedwait

  1. 原型:int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

  2. 功能:定时阻塞去申请资源 阻塞等待申请abs_timeout时间后不在阻塞等待,就报错返回

  3. 参数:

    ​ sem :目标信号灯

    ​ bs_timeout:时间的结构体 ,

    ​ tv_sec+ 你想要定时的时间

  4. 返回值:

    ​ 成功返回0

    ​ 失败返回-1;

📝练习:共有三个线程,1.2.3线程分别打印 A B C,循环打印后,最终输出结果是BCA BCA BCA

代码:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
int count = 0;//全局变量
//定义无名信号灯
sem_t sem1; // 给线程1使用,初值应该为1
sem_t sem2;// 给线程2使用,初值应该为0
sem_t sem3;//给主线程使用,初值应该为0
//线程函数

void *myfun1(void *arg)  //线程函数
{
    while (1)
    {
        sem_wait(&sem1);
        printf("B");
        sem_post(&sem2);
    }
}
void *myfun2(void *arg)  //线程函数
{
    while (1)
    {
        sem_wait(&sem2);
        printf("C");
        sem_post(&sem3);
    }
}

int main(int argc, const char *argv[])
{
    //初始化无名信号灯,分别初始化值为1 0 
    sem_init(&sem1,0,1);
    sem_init(&sem2,0,0);
    sem_init(&sem3,0,0);
    //开始创建线程
    pthread_t tid = 0;
    pthread_t tid1 = 0;
    if(0 != pthread_create(&tid,NULL,myfun1,NULL))
    {
        perror("creat");
        return -1;
    }

    if(0 != pthread_create(&tid1,NULL,myfun2,NULL))
    {
        perror("creat");
        return -1;
    }

    //主线程干自己的事情

    while(1)
    {
        sem_wait(&sem3);
        printf("A\n");
        sem_post(&sem1);
        sleep(1);
    }

    return 0;
}
linux@ubuntu:~/demo/test/IO/test$ gcc test.c -lpthread
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
BCA
BCA
BCA
BCA

线程间的互斥

互斥:与同步机制不同的点在于无需顺序执行必须保证同一时间内只允许有一个线程去访问临界资源。实质上是保证同一临界区只允许同一时间运行一个

  • 临界资源:多线程共享的易改变的资源。

  • 临界区 :修改临界资源的代码

📣注意:同步一定会互斥,但是互斥不一定同步

互斥机制:互斥锁

线程1和线程2都要操作临界区的a。

注意:谁先到先给谁,先申请,然后归还。如果线程1先到,则申请加锁,操作a,这时候假如线程2到了就堵塞等待加锁。

Screenshot_20210828_161826_com.fluidtouch.noteshe.jpg

1.pthread_mutex_init

  1. 原型:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);

  2. 功能:动态初始化一把锁

  3. 参数:

    ​ mutex:锁变量的地址

    ​ mutexattr:互斥锁的属性,NULL缺省默认

  4. 返回值: 总是返回0

2.静态初始化一把锁

pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;

3.pthread_mutex_lock

  1. 原型:int pthread_mutex_lock(pthread_mutex_t *mutex);

  2. 功能:尝试加锁,如果没有抢到锁资源,阻塞等待加锁

  3. 参数:mutex 锁的地址

  4. 返回值:

    ​ 成功返回0

    ​ 失败返回非0

4.pthread_mutex_trylock

  1. 原型: int pthread_mutex_trylock(pthread_mutex_t *mutex);

  2. 功能:尝试加锁,如果没有申请到锁资源,报错返回

  3. 参数:锁的地址

  4. 返回值:

    ​ 成功返回0

    ​ 失败返回非0

5.pthread_mutex_unlock

  1. 原型:int pthread_mutex_unlock(pthread_mutex_t *mutex);

  2. 功能:解锁操作

  3. 参数:锁的地址

  4. 返回值:

    ​ 成功返回0

    ​ 失败返回非0

6.pthread_mutex_destroy

  1. 原型:int pthread_mutex_destroy(pthread_mutex_t *mutex);

  2. 功能:销毁一把锁

  3. 参数:锁的地址

  4. 返回值:

    ​ 成功返回0

    ​ 失败返回非0

📝练习:目标将全局变量a加到100 0000 ,使用两个线程去做,每个线程应该各加50 0000次,这种情况还需要同步吗?

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

int count = 0;

//初始化一把锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void * myfun (void *arg)
{
    int i = 500000;
    while(i)
    {
        //临界区 --开始 尝试加锁操作
        if(-1 == pthread_mutex_lock(&mutex))
        {
            perror("lock");
            return NULL;
        }
        
        int a = count;
        a++;
        count = a;
        
         //临界区 --截止
        if(-1 == pthread_mutex_unlock(&mutex))
        {
            perror("unlock");
            return NULL;
        }
        i--;
    }
}

int main(int argc, const char *argv[])
{
    //创建一个线程
    pthread_t tid = 0;
    if(0 != pthread_create(&tid,NULL,myfun,NULL))\
    {
        perror("create");
        return -1;
    }


        int i = 500000;
        while(i)
        {
             //临界区 --开始 尝试加锁
            pthread_mutex_lock(&mutex);
            int a = count;
            a++;
            count = a;
             //临界区 --截止
            pthread_mutex_unlock(&mutex);
            i--;
        }

    pthread_join(tid,NULL);
     //销毁这把锁头
    pthread_mutex_destroy(&mutex);

    printf("count的值为%d\n",count);

    return 0;
}
linux@ubuntu:~/demo/test/IO/test$ gcc test.c -lpthread
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
count的值为1000000