一、线程同步

       同步:多进程或者多线程访问临界资源时,必须进行同步控制。多进程或者多线 程的执行并不完全是绝对的并行运行,有可能

线程需要等待函数线程的某些条件的发生。

      多线程之间有几个特殊的临界资源:

             全局数据、堆区数据、文件描述符   多线程之间共用

      线程间同步控制方式:

      1.1 信号量  

            用到的头文件 #include <semaphore.h>

            获取: int sem_init(sem_t *sem, int shared, int value);

                             sem: 是一个 sem_t 类型指针,指向信号量对象。

                             shared: 是否能在多进程间共享, Linux 不支持,为0

                             value: 信号量的初始值

                             返回值: 成功返回0,失败返回-1

            P 操作: int sem_wait(sem_t *sem);// 阻塞运行

            V 操作: int sem_post(sem_t *sem);

            删除: int sem_destroy(sem_t*sem);

      1.2 互斥锁

            概念:完全控制临界资源,如果一个线程完成加锁操作,则其他线程无论如何 都无法再完成加锁,也就无法对临界资源进行访问。

            初始化:int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutex_attr_t *attr);

                          第二个参数是指锁的属性,一般默认为NULL

            加锁:int pthread_mutex_lock(pthread_mutex_t *mutex);// 阻塞运行

                       int pthread_mutex_trylock(pthread_mutex_t *mutex);//非阻塞版本

            解锁:int pthread_mutex_unlock(pthread_mutex_t*mutex);

            释放:int pthread_mutex_destroy(pthread_mutex_t*mutex);

      1.3 条件变量

2、线程安全 

     可重入函数:有些库函数会使用线程间共享的数据,如果没有同步控制,线程操作就是不安全的, 所以,我们使用

      这样一些函数时,就必须使用其安全的版本 --->可重入函数

线程控制(二)

      例如 rand 的可重入函数为 rand_r

      srand函数为rand函数指定种子,如果没指定,rand函数默认的种子为1,那么每次产生的随机序列是相同的。srand以时间作为

随机数的种子,使得rand每次取到的随机序列都不一样。但由于每调用一次rand会改变它的状态值(如next值),而对于同一进程的

多个线程共享这些状态值,所以rand函数是非线程安全的,多线程应该明确指定seed值的rand_r。

       再比如 strtok 的可重入函数为 strtok_r

       子串末尾的下一个字符提取前是分隔符,提取后被修改成了'\0'。

       没有提取到子串,即源字符串中没有分隔符字符串的分隔符,返回的是源字符串的首地址。与预期不符。

       strtok_r比stryok多的一个参数是一个指向char *的指针变量,用来在strtok_r内部保存切分时的上下文,以应对连续调用分解相同源字符串。

       strtok_r实际上就是将strtok内部隐式保存的分隔符下一位的指针,以参数的形式与函数外部进行交互。

3、线程中 fork 的使用,锁的变化

      在线程中调用 fork 函数,子进程只会启用调用 fork 函数的那条线程,其他线程不会启用。

      子进程会继承其父进程的锁以及锁的状态,但是父子进程用的不是同一把锁,父进 程解锁并不会影响到子进程的锁,所以子进

有可能死锁!!!

       解决方案:

       pthread_atfork(void(*prepare)(void),void(*parent)(void),void(*child)(void));

      指定在fork调用之后,创建子进程之前, 调用 prepare 函数,获取所有的锁,然后创建子进程,子进程创建以后,父进程环境中调

用 parent 解所有的锁,子进程环境中调用child解所有的锁,然后fork函数再回。这样保证了fork之后,子进程拿到的锁都是解锁状

态,避免死锁。

相关文章:

  • 2022-12-23
  • 2021-09-22
  • 2022-12-23
  • 2021-10-12
  • 2021-10-29
  • 2021-12-24
  • 2021-11-11
  • 2021-12-18
猜你喜欢
  • 2021-07-11
  • 2021-08-25
  • 2019-09-13
相关资源
相似解决方案