您的位置:首页 > 房产 > 建筑 > 木马设计公司官网_在线制作logo网站_网络营销的概念和特点是什么_网站推广搜索

木马设计公司官网_在线制作logo网站_网络营销的概念和特点是什么_网站推广搜索

2025/7/23 13:53:10 来源:https://blog.csdn.net/Einsteinznm/article/details/147022885  浏览:    关键词:木马设计公司官网_在线制作logo网站_网络营销的概念和特点是什么_网站推广搜索
木马设计公司官网_在线制作logo网站_网络营销的概念和特点是什么_网站推广搜索

1.补充点:

上述这些函数成功返回0,失败则返回错误码;

pthread_mutex_destroy函数用于销毁互斥锁,以释放其占用的内核资源.

销毁一个已经加锁的互斥锁将导致不可预期的后果.

2.线程安全引例

先来看这样一段代码的执行结果是什么?

#include  <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <assert.h>#include <pthread.h>void*thread_fun(void *arg){char buff[128]={"a b c d e f g h w q"};char *s=strtok(buff," ");while(s!=NULL){printf("thread:s=%s\n",s);sleep(1);s=strtok(NULL," ");}}int main(){pthread_t id;pthread_create(&id,NULL,thread_fun,NULL);char str[128]={"1 2 3 4 5 6 7 8 9 10"};char *s=strtok(str," ");while(s!=NULL){printf("main:%s\n",s);sleep(1);s=strtok(NULL," ");}pthread_join(id,NULL);exit(0);}

 

 

为什么是这样的结果呢?

因为strtok不是线程安全的.内部使用了全局变量(静态变量);  也就是说,只要使用了全局变量或者静态变量的函数都不能在多线程中使用;这些函数都不是线程安全的.

不可重入:当程序被多个线程反复调用,产生的结果会出错.

3线程安全

线程安全即就是在多线程运行的时候,不论线程的调度顺序怎样,最终的结果都是一样的、正确的。那么就说这些线程是安全的。

要保证线程安全需要做到:

1) 对线程同步,保证同一时刻只有一个线程访问临界资源.

2) 在多线程中使用线程安全的函数(可重入函数).

所谓线程安全的函数指的是:如果一个函数能被多个线程同时调用且不发生竟态条件,则我们程它是线程安全的。

4.使用线程安全的函数

strtok_r

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <pthread.h>void *thread_fun(void *arg)
{char buff[128]={"a b c d e f g h w q"};char *ptr=NULL;char *s=strtok_r(buff," ",&ptr);while(s!=NULL){printf("thread:s=%s\n",s);sleep(1);s=strtok_r(NULL," ",&ptr);}
}
int main()
{pthread_t id;pthread_create(&id,NULL,thread_fun,NULL);char str[128]={"1 2 3 4 5 6 7 8 9 10"};char *ptr=NULL;char *s=strtok_r(str," ",&ptr);while(s!=NULL){printf("main:%s\n",s);sleep(1);s=strtok_r(NULL," ",&ptr);}pthread_join(id,NULL);exit(0);
}

 

5.多线程中执行fork

思考下面两个问题:

(1)多线程中某个线程调用 fork(),子进程会有和父进程相同数量的线程吗?

(2)父进程被加锁的互斥锁 fork 后在子进程中是否已经加锁?

1.多线程中执行fork,两个面试题

解析1: 代码验证:

线程主要是做一个输出,验证线程的个数;

#include <stdio.h>#include <stdlib.h>#include<unistd.h>#include <string.h>#include <assert.h>#include <pthread.h>void *fun(void *arg){int i=0;for(;i<5;i++){printf("fun run pid=%d\n",getpid());//打印当前进程的PID;sleep(1);}}int main(){pthread_t id;pthread_create(&id,NULL,fun,NULL);fork();int i=0;for(;i<5;i++){printf("main run pid=%d\n",getpid());sleep(1);}}

 

main run pid=4670    有5个

fun run pid=4670    有5个

main run pid=4672    有5个

再把fork挪动到子线程中观察一下.

结论(1):

fork以后,不管父进程有多少条执行路径,子进程只有一条执行路径.这条执行路径就是fork所在的那条执行路径; 

解析2:

关于第二个问题,我们先来看下面一段代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/wait.h>pthread_mutex_t mutex;//在这个线程中,我们模拟持有锁5秒中
void* fun(void *arg)
{pthread_mutex_lock(&mutex);printf("fun lock\n");sleep(5);pthread_mutex_unlock(&mutex);printf("fun unlock\n");
}int main()
{pthread_t id;pthread_mutex_init(&mutex,NULL);pthread_create(&id,NULL,fun,NULL);sleep(1);//保证子线程一定已经加锁了;pid_t pid=fork();//创建一个子进程;if(pid==-1){exit(1);//如果fork失败了,那么我们就退出进程,也就是说主线程和子线程都退出;}if(pid==0){printf("child lock start\n");pthread_mutex_lock(&mutex);printf("child lock success\n");pthread_mutex_unlock(&mutex);exit(0);}wait(NULL);pthread_join(id,NULL);printf("main over\n"); exit(0);
}

 

 为什么呢?

原因如下:

 

其实就是:fork之后锁的状态也一并被复制了.但是.

因为多进程并发运行,你也不知道某一刻锁的状态到底是什么;也就是锁的状态在子进程中是不清晰的;也就是子进程中锁的状态你也不清楚,那么我们怎么在子进程中使用锁呢?

虽然你可以直接解锁,但是这么做意义就不对了,如果本来是在保护资源,你一来就解锁,那么程序就出现问题了;

结论(2):

父进程有锁,子进程也被复制了锁;锁的状态取决于fork的那一刻父进程的锁的状态.也就是说锁的状态也会被复进去子进程 ;

2.如何解决呢?(拓展了解)

延迟fork的复制.

没有人用锁的时候我们再去fork;那么如何判断有没有人用锁呢?我们去加锁一下,如果没有成功,就是有人用锁.如果加锁成功,就是没有人用锁.这个时候再去fork;

而这个方法(在fork前后去加锁),它是有一个线程的方法可以完成的:pthread_atfork;

man  pthread_atfork

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

三个参数:每个参数都是一个函数指针;指针指向参数为void,返回值也为void的函数; 

//先准备两个函数
void prepare(void)
{pthread_mutex_lock(&mutex);
}
void after(void)
{pthread_mutex_unlock(&mutex);
}
//然后再主函数中对fork进行如下的设置:(放在锁的初始化后面,fork之前即可)
pthread_atfork(prepare,after,after);

6.死锁(重点) 

死锁主要发生在有多个依赖锁存在时,会在一个线程试图以另一个线程相反顺序锁住互斥量时发生.

死锁使得一个或多个线程被挂起而无法继续执行,最糟糕的是,这种情况还不容易被发现;

在一个线程中对一个已经加锁的普通锁再次加锁,将导致死锁.

这种情况可能出现在设计得不够仔细地递归函数中.

另外,如果两个线程按照不同的顺序来申请两个互斥锁,也容易发生死锁.

代码如下:

#include <stdio.h>
#include <unistd.h>
#include <stdio.h>int a=0;
int b=0;
pthread_mutex_t  mutex_a;
pthread_mutex_t  mutex_b;void *another(void *arg)
{pthread_mutex_lock(&mutex_b);printf("in child thread,got mutex b,waiting for mutex a\n");sleep(5);++b;pthread_mutex_lock(&mutex_a);b+=a++;pthread_mutex_unlock(&mutex_a);pthread_mutex_unlock(&mutex_b);pthread_exit(NULL);
}int main()
{pthread_t id;pthread_mutex_init(&mutex_a,NULL);pthread_mutex_init(&mutex_b,NULL);pthread_create(&id,NULL,another,NULL);pthread_mutex_lock(&mutex_a);printf("in parent thread,got mutex a,waiting for mutex b\n");sleep(5);++a;pthread_mutex_lock(&mutex_b);a+=b++;pthread_mutex_unlock(&mutex_b);pthread_mutex_unlock(&mutex_a);pthread_join(id,NULL);pthread_mutex_destroy(&mutex_a);pthread_mutex_destroy(&mutex_b);exit(0);
}

 主线程试图先占有互斥锁mutex_a,然后操作被该锁保护的变量a,但操作完毕之后,主线程并没有立即释放互斥锁mutex_a,而是又申请互斥锁mutex_b,并在两个互斥锁的保护下,操作变量a和b,最后才一起释放这两个互斥锁;

于此同时,子线程则按照相反的顺序来申请互斥锁mutex_a和mutex_b,并在两个锁的保护下操作变量a和b.

我们用sleep函数来模拟连续两次调用pthread_mutex_lock之间的时间差,以确保代码中的两个线程各自先占用一个互斥锁(主线程占有mutex_a,子线程占有mutex_b),然后等待另外一个互斥锁(主线程等待mutex_b,子线程等待mutex_a).这样,两个线程就僵持住了,谁也不能继续往下执行,从而形成死锁.

如果代码中不加入sleep函数,则这段代码或许总能成功地运行,从而为程序留下了一个潜在的bug.

如何避免死锁是使用互斥量应该格外注意的东西. 总体来讲,有几个不成文的基本规则: 对共享资源操作前一定要获得锁. 完成操作以后一定要释放锁. 尽量短时间地占用锁. 如果有多个锁,如获得顺序是ABC连环扣.释放顺序也应该是ABC. 线程错误返回时应该释放它所获得的锁.

死锁相关面试题目(重点):

问题:如何避免死锁?

解答: 死锁的概念:多个进程或线程访问一组竟态资源的时候,出现的永久阻塞的问题。也可以这么说:指两个或两个以上的线程或进程在执行程序的过程中,因争夺资源或者程序推进顺序不当而相互等待的一个现象。 产生的原因主要有三个:系统资源不足,程序运行推进的顺序不当,资源分配不当。或者说:死锁产生的必要条件是:互斥条件、请求和保持条件、不剥夺条件、环路等待条件. 避免死锁就是打破这四个条件中的某一个即可。如某个进程申请多个资源,只要有一个资源不满足暂时就不要分配任何资源。等所有资源能满足时一起分配。

处理死锁的基本方法?

预防死锁、避免死锁(银行家算法)、检测死锁(资源分配)、解除死锁:剥夺资源、撤销进程.

7.拓展部分(仅作了解:线程属性)

如你所见,可以使用的线程属性非常多.但幸运的是,你通常不需要设置太多属性就可以让程序正常工作.

 

 我们一般是用不到线程的属性的;

先看前面我们讲过的例子:

#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <unistd.h>#include <assert.h>void *fun(void *arg){int i=0;for(;i<10;i++){printf("fun run\n");sleep(1);}pthread_exit(NULL);}int main(){pthread_t id;pthread_create(&id,NULL,fun,NULL);int i=0;for(;i<5;i++){printf("main run\n");sleep(1);}// pthread_join(id,NULL);//注意,一定要屏蔽这一行;  exit(0);}

detachedstate:这个属性允许我们无需对线程进行重新合并.

例如:

我们对线程做如下设置:

pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);pthread_create(&id,&attr,fun,NULL);

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com