当前位置: 首页 > news >正文

厦门网站个人制作wordpress生成标签页

厦门网站个人制作,wordpress生成标签页,建企业网站浩森宇特,电商网络营销是干什么的文章目录 线程安全问题的引入线程互斥互斥概念互斥锁互斥锁的计数器当中如何保证原子性互斥锁基础API初始化互斥锁变量函数动态初始化静态初始化 加锁函数阻塞加锁非阻塞加锁带有超时时间的加锁 解锁函数销毁互斥锁函数 线程同步线程同步的必要性条件变量条件变量的使用原理条件… 文章目录 线程安全问题的引入线程互斥互斥概念互斥锁互斥锁的计数器当中如何保证原子性互斥锁基础API初始化互斥锁变量函数动态初始化静态初始化 加锁函数阻塞加锁非阻塞加锁带有超时时间的加锁 解锁函数销毁互斥锁函数 线程同步线程同步的必要性条件变量条件变量的使用原理条件变量的原理条件变量基础API初始化条件变量函数动态初始化静态初始化 销毁条件变量函数等待条件变量函数唤醒条件变量函数单个唤醒广播唤醒 条件变量常见问题 线程安全问题的引入 使用一个 抢票程序 演示线程安全的概念及重要性 代码如下 1 #includestdio.h2 #includeunistd.h3 #includepthread.h4 int g_tickets 100; W 5 void* mythread_start(void* arg){6 while(1){7 if(g_tickets 0)8 break; W 9 printf(I am %p, I have ticket %d\n, pthread_self(),g_tickets--);10 //usleep(100);11 //--g_tickets;12 } W 13 } 14 int main(){ W 15 int i 0;16 pthread_t tid[2];17 for(int i0; i2; i){18 int ret pthread_create(tid[i], NULL, mythread_start, NULL); 19 if(ret 0){ 20 perror(pthread_create);21 return 0; 22 } 23 for(int i0; i2; i){ 24 pthread_join(tid[i],NULL);//设置为阻塞25 }26 }27 return 0;28 }执行结果 结果分析 我们代码的预期目标是创造两个黄牛线程让这两个线程去抢票我们观察执行可以不难看出抢票活动只有一个黄牛在参与两外一个线程并没有参与这是怎么回事呢这是正常现象吗 首先这是正常的现象和结果我们称两个线程分贝为A和BA首先被创建出来A被创建出来后是不会等B创建出来再一起去抢票的我们之前强调过线程是被操作系统独立调度的所以先被创建出来的A线程就先执行了抢票程序拿到了全部的票。 那什么样的结果算是有问题的呢A和B抢到了同一张票或者抢到了不合法的票负数 为了让两个黄牛都能抢到票我们每次抢票结束后都让黄牛休息一下 代码如下 1 #includestdio.h2 #includeunistd.h3 #includepthread.h4 int g_tickets 100; W 5 void* mythread_start(void* arg){6 while(1){7 if(g_tickets 0)8 break; W 9 printf(I am %p, I have ticket %d\n, pthread_self(),g_tickets);10 usleep(100);11 --g_tickets;12 } W 13 } 14 int main(){ W 15 int i 0;16 pthread_t tid[2];17 for(int i0; i2; i){18 int ret pthread_create(tid[i], NULL, mythread_start, NULL); 19 if(ret 0){ 20 perror(pthread_create);21 return 0; 22 } 23 for(int i0; i2; i){ 24 pthread_join(tid[i],NULL);//设置为阻塞25 }26 }27 return 0;28 }执行结果 出现了段错误这是什么原因呢因为我们出现了二义性在同一时刻两个线程对同一个数据进行修改。 错误分析 1、假设同一个程序中有两个线程A和BA和B同时对一个int类型的全局变量n10在其各自的线程入口函数中对这样一个全局变量进行加加操作 2、当A拥有CPU之后对n进行操作是非原子性的操作也就是说这个操作随时可能会被打断假设A被调度刚把全局变量的数值10读取到CPU的寄存器中时就被切换出去了程序计数器中保存着线程A下一步要执行的指令上下文信息中保存寄存器的值这两个东西是为了当线程A再次拥有CPU使用权的时候还原当时线程A切换出去的线程现场使用的 3、当线程A被切换出去后这会儿可能线程B获取了CPU时间片被调度B对全局变量进行了操作此时全局变量从10变成了11并且回写到内存中去 4、当线程A再次拥有CPU时间片之后恢复当时切换出去的现场继续往下执行由于上下文信息中保存的全局变量仍然是10执行完操作变成11然后再回写到内存中去全局变量的值仍任是11 5、虽然两个线程都对这个全局变量进行了操作但是从值上面来看全局变量仅仅进行了一次操作这就是线程的不安全 线程互斥 互斥概念 互斥要做的事情控制线程的访问时序保证各个线程对共享资源的独占式访问。当多个线程能够同时访问到临界资源的时候有可能会导致线程执行的结果产生二义性。而互斥就是要保证多个线程在访问同一个临界资源执行临界区代码的时候 (非原子性性操作(线程可以被打断) )控制访问时序。让一个线程独占临界资源执行完再让另外一个独占执行。 临界资源能被多个线程同时访问到的资源 临界区代码访问临界资源的代码 互斥锁 互斥锁的本质就是0/1计数器计数器的取值只能为0/1 0表示当前线程不可以获取到互斥锁也就不能访问临界区资源1表示当前线程可以获取到互斥锁可以访问临界资源 需要注意的是并不是说线程不获取互斥锁就不能访问临界资源而是程序员需要在代码中用同一个互斥锁去约束多个线程意思就是说在加锁时必须加的是同一把锁当线程A拿到这把锁将这把锁的信号量改成0其他线程就无法访问了但是当线程A被切换出去之后其他线程加锁加的也是这把被A置为0的锁只有当A访问结束将这把锁置为1其他线程才可以访问临界区资源否则A加锁访问B访问临界资源之前不加锁这样也不能约束B这就叫防君子不防小人也就是说在每一个线程代码中要访问一个临界区资源之前要先获取锁也就是加锁。 为了避免出现两个线程可能同时加锁成功我们需要互斥锁本身就是原子性的。 互斥锁的计数器当中如何保证原子性 为什么计数器锁当中的值从0变成1,或者从1变成0是原子性的呢? 为了保证互斥锁操作大多数体系结构都提供了swap和exchange指令该指令作用是把寄存器和内存单元的数据相交换由于只有一条指令只会出现执行成功和未执行两种情况所以是原子性的 加锁的时候将寄存器当中的值设置为0 第一种情况计数器的值为1说明锁空闲没有被线程加锁交换情况加锁成功第二种情况计数器的值为0说明锁忙碌被其他线程加锁拿走此时当前线程进入等待状态等待其他线程访问完毕将锁打开交换情况加锁失败 解锁的时候将寄存器当中的值设置为1 计数器的值为0需要解锁 进行一步交换交换成功解锁成功交换失败解锁失败 互斥锁基础API 互斥锁的相关函数主要有以下5个 #include pthread.h int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); int pthread_mutex_destroy(pthread_mutex_t *mutex); int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex);这些函数的第一个参数mutex都是一个pthread_mutex_t结构体指针变量指向要操作的目标互斥锁结构体参数attr是一个pthread_mutexattr_t结构指针变量指向互斥锁属性结构体如果填入NULL表示使用默认属性 初始化互斥锁变量函数 动态初始化 int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);功能初始化一个互斥锁结构体的属性 参数 pthread_mutex_t* : 一个指向pthread_mutex_t结构体的指针变量pthread_mutexattr_t* 一个指向pthread_mutexattr_t结构的指针变量 返回值成功返回0失败返回errno 静态初始化 使用宏PTHREAD_MUTEX_INITIALIZER来初始化一个互斥锁结构体实际上该宏是将互斥锁的各个字段初始化为0 pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER; //初始化一个mutex锁宏定义源码 #define PTHREAD_MUTEX_INITIALIZER \ { { 0, 0, 0, 0, 0, __PTHREAD_SPINS, { 0, 0 } } }加锁函数 阻塞加锁 int pthread_mutex_lock(pthread_mutex_t *mutex);功能给临界区代码阻塞等待加锁 参数pthread_mutex_t* : 一个指向pthread_mutex_t结构体的指针变量 返回值返回0表示成功加锁失败返回errno 阻塞加锁解释 如果mutex中互斥量的值为1则pthread_mutex_lock函数就返回0表示加锁成功如果mutex中互斥量的值为0则线程阻塞在pthread_mutex_lock函数中直到加锁成功 非阻塞加锁 int pthread_mutex_trylock(pthread_mutex_t *mutex);功能给临界区代码非阻塞加锁 参数pthread_mutex_t* : 一个指向pthread_mutex_t结构体的指针变量 返回值返回0表示成功加锁失败返回errno 非阻塞加锁解释 如果mutex中互斥量的值为1则pthread_mutex_trylock函数就返回0表示加锁成功如果mutex中互斥量的值为0则pthread_mutex_trylock函数就返回错误码EBUSY 非阻塞加锁拿锁的时候如果拿不到锁就直接返回了也不等待需要搭配循环使用一直调用当其返回值为zero时循环结束否则使用这个接口的后果就和我们上面说的当一个进程对一块被占用的资源访问时如果不拿到这个锁也是可以访问的这样就达不到互斥访问的目的了 带有超时时间的加锁 int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout);功能如果不能立即获得目标互斥锁则等待abs_timeout时间如果在等待时间内加锁成功则直接返回如果超过等待时间则直接返回表示加锁失败 解锁函数 int pthread_mutex_unlock(pthread_mutex_t *mutex);功能将互斥锁中的互斥量从0变成1表示其他线程可以获取该互斥锁了。以原子操作的方式给一个互斥锁解锁如果此时有其他线程正在等待这个互斥锁则其他线程会获得该互斥锁 参数pthread_mutex_t* : 一个指向pthread_mutex_t结构体的指针变量 返回值返回0表示解锁成功解锁失败返回errno 销毁互斥锁函数 int pthread_mutex_destroy(pthread_mutex_t *mutex);功能将目标互斥锁销毁 参数pthread_mutex_t* : 一个指向pthread_mutex_t结构体的指针变量 返回值销毁成功返回0销毁失败返回errno 如果是动态初始化互斥锁就需要销毁如果是静态初始化就不用销毁。 在所有线程可能退出的地方进行解锁防止产生死锁 代码演示 我们对上面的黄牛抢票的程序进行修改代码如下 1 #includestdio.h2 #includeunistd.h3 #includepthread.h4 int g_tickets 100;5 pthread_mutex_t lock; W 6 void* mythread_start(void* arg){7 pthread_mutex_lock(lock);//加锁8 while(g_tickets--){ W 9 printf(I am %p, I have ticket %d\n, pthread_self(),g_tickets);10 } W 11 }12 int main(){13 pthread_mutex_init(lock, NULL);//动态初始化14 pthread_t tid[2];15 for(int i0; i2; i){ 16 int ret pthread_create(tid[i], NULL, mythread_start, NULL);17 if(ret 0){18 perror(pthread_create);19 return 0;20 }21 }22 while(1){23 sleep(1);24 }25 pthread_mutex_destroy(lock);//退出时销毁互斥锁26 return 0;27 } 执行结果 结果分析 我们查看进程的调用栈发现除了主线程外只有一个线程这个工作线程一直在等待锁但是加锁的线程已经将锁锁上而且执行完代码后退出了现在这把锁就被永远的锁上这种情况叫死锁 上述导致死锁的原因是线程退出时没有进行解锁那防止死锁的产生也就需要在线程所有可能退出的地方进行解锁。 我们对代码进行改进前五十张票让第一个线程拿也就是全局变量小于50时第一个线程进行解锁让另一个线程可以接手修改代码如下 执行结果 这下子并没有再出现死锁的情况了 结论在线程所有有可能退出的地方都进行解锁防止产生死锁不要让线程退出的时候把锁带走 线程同步 线程同步的必要性 多个线程保证了互斥 也就是保证了线程能够合理的访问临界资源了。但并不是说 各个线程在访问临界资源的时候都是合理的。同步是为了保证多个线程对临界资源的访问的合理性这个合理性建立在多个线程保证互斥的情况下。就比如说一个吃面的场景当一个只有一个碗吃面和做面的人都可以对这个碗进行访问操作而对这个碗同时只能有一个人访问不可以同时做面的人在往碗里做面吃面的人也在碗中吃面要防止这样的情况发生就要采用互斥的原理但是当有了互斥之后保证线程可以独自访问资源了就如吃面的人可以独自吃面了而不会有做面的人来干扰而做面的人也可以独自做面了也不会有吃面的人同时和它抢生意但是还有一个问题就是你吃面的人不可以在碗里没有面的情况下去吃面甚至把碗吃掉你做面的人不可以在碗里有面的情况下再去做面那碗里都盛不下面了所以此时要有同步的概念来保证访问临界资源的合理性。 同步保证各个线程对于共享资源的访问具有合理性 模拟一下上面的场景代码如下 1 #includestdio.h2 #includepthread.h3 #includeunistd.h4 pthread_mutex_t g_bowl;//互斥锁碗5 int bowl 1; W 6 void* eat_thread_start(void* arg){7 while(1){8 pthread_mutex_lock(g_bowl);9 printf(I am eatman eat%d\n,bowl--);10 pthread_mutex_unlock(g_bowl);11 }12 } W 13 void* make_thread_start(void* arg){14 while(1){15 pthread_mutex_lock(g_bowl);16 printf(I am makeman make%d\n,bowl);17 pthread_mutex_unlock(g_bowl);18 }19 }20 int main(){ 21 pthread_mutex_init(g_bowl, NULL);22 pthread_t eat_tid;23 pthread_t make_tid;24 int ret pthread_create(eat_tid, NULL, eat_thread_start, NULL);25 if(ret 0){26 perror(pthread_create);27 }28 ret pthread_create(make_tid, NULL, make_thread_start, NULL);29 if(ret 0){30 perror(pthread_create);31 }32 pthread_join(eat_tid, NULL);33 pthread_join(make_tid, NULL);34 pthread_mutex_destroy(g_bowl);35 return 0;36 } 执行结果 做面做出了负数出现了不合理的访问。 为了控制线程对共享资源的独占式访问,并且访问次序具有合理性有以下方式实现线程同步 POSIX信号量条件变量 什么是对共享资源的独占式访问呢 同学们在上厕所的时候坑位就是一个共享资源同学们在上厕所时将厕所门关上就是对共享资源的独占式访问在你上厕所的时候别人进不来无法访问引申到代码中就是相当将对于一个变量的操作变成原子性操作要么操作成功要么没操作不存在操作一半被打断的情况 条件变量 条件变量的使用原理 线程在加锁后判断下临界资源是否可用如果可用则直接访问临界资源如果不可用则调用等待接口让该线程进行等待 改进代码 执行结果可以看到这里就正常了。做面人不会因为碗里有面再做面吃面人不会因为碗里没有面而把碗吃掉 但是上述代码有很严重的效率缺陷十分耗费CPU资源还不拿CPU资源干正事儿假设此时一个线程拿到的时间片是200ms该线程在该时间片内判断吃面人要吃面但是如果碗里没面他就continue退出这一次循环但是由于时间片没有结束就会再次进行循环加锁判断是否有面但是还是没有面就再次退出循环如此反复假设一次他加锁后还没进行解锁时间片就用完了下一个线程要执行要拿到互斥锁但是此时互斥锁没有被释放这个线程又要在所有的时间片内重复判断但是这些举动是毫无意义的程序效率就很低下 条件变量的原理 条件变量提供了一种线程间的通知机制当某个共享数据达到某个值的时候唤醒等待这个共享数据的线程 条件变量本质是一个PCB等待队列(存放在等待的线程的PCB) 条件变量基础API 条件变量的相关函数主要以下几个 #include pthread.h int pthread_cond_init(pthread_cond_t * cond,pthread_condattr_t * attr); pthread_cond_t cond PTHREAD_COND_INITIALIZER;int pthread_cond_destroy(pthread_cond_t *cond);int pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, struct timespec * abstime); int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex);int pthread_cond_broadcast(pthread_cond_t *cond); int pthread_cond_signal(pthread_cond_t *cond);pthread_cond_t是条件变量结构体pthread_condattr_t是条件变量属性结构体PTHREAD_COND_INITIALIZER是一个宏用来初始化条件变量结构体的本质是将条件变量各个字段设置为0 初始化条件变量函数 动态初始化 int pthread_cond_init(pthread_cond_t* cond, pthread_condattr_t* attr);功能初始化条件变量结构体 参数 pthread_cond_t : 条件变量结构体指针pthread_condattr_t 条件变量属性结构体指针常传入NULL使用默认的属性 pthread_cond_t cond; pthread_cond_init(cond,NULL);静态初始化 使用宏PTHREAD_COND_INITIALIZER初始化条件变量 pthread_cond_t cond PTHREAD_COND_INITIALIZER;销毁条件变量函数 int pthread_cond_destroy(pthread_cond_t *cond);功能销毁一个条件变量 参数pthread_cond_t : 条件变量结构体指针 pthread_cond_destroy(cond)等待条件变量函数 int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex);功能将调用该函数的线程放入PCB等待队列中 参数 pthread_cond_t : 条件变量结构体指针 mutex : 该线程等待的互斥锁 唤醒条件变量函数 单个唤醒 int pthread_cond_signal(pthread_cond_t *cond);功能唤醒一个等待目标条件变量的线程至于唤醒的是PCB等待队列当中的哪个线程取决于线程的优先级和调度策略有可能唤醒两个或者三个或者全部都唤醒 参数pthread_cond_t : 条件变量结构体指针 广播唤醒 int pthread_cond_broadcast(pthread_cond_t *cond);功能以广播的方式唤醒所有等待目标条件变量的线程 参数pthread_cond_t : 条件变量结构体指针 代码演示 执行结果吃面人吃面做面人做面很和谐 将上述程序的进程数量增加比如说有两个做面人和两个吃面人此时又会出现新的问题当wait结束后执行的是printf也就是吃面/做面再之后我们的if判断就不起作用了就会出现疯狂的做面和吃面所以我们在wait结束后printf之前也就是吃面/做面前再次判断是否能吃面/做面 代码如下 #includestdio.h #includepthread.h #includeunistd.h #define countman 2 pthread_mutex_t g_bowl;//互斥锁碗 pthread_cond_t g_cond;//条件变量 int bowl 1; void* eat_thread_start(void* arg){while(1){pthread_mutex_lock(g_bowl);if(bowl0){//如果碗里没有面了就不能吃面了pthread_mutex_unlock(g_bowl);pthread_cond_wait(g_cond, g_bowl);//没面了等做面人做面}printf(I am eatman eat%d\n,bowl--);pthread_mutex_unlock(g_bowl);pthread_cond_signal(g_cond);//面吃完了通知做面人来做面} } void* make_thread_start(void* arg){while(1){pthread_mutex_lock(g_bowl);if(bowl1){//如果碗里有面就不再做面了pthread_mutex_unlock(g_bowl);pthread_cond_wait(g_cond, g_bowl);//等待吃面人吃面没碗装面了}printf(I am makeman make%d\n,bowl);pthread_mutex_unlock(g_bowl);pthread_cond_signal(g_cond);//面做好了通知吃面人吃面} } int main(){pthread_mutex_init(g_bowl, NULL);pthread_t eat_tid[countman];pthread_t make_tid[countman];for(int i0; icountman; i){int ret pthread_create(eat_tid[i], NULL, eat_thread_start, NULL);if(ret 0){perror(pthread_create);}ret pthread_create(make_tid[i], NULL, make_thread_start, NULL);if(ret 0){perror(pthread_create);}}for(int i0; icountman; i){pthread_join(eat_tid[i], NULL);pthread_join(make_tid[i], NULL);}pthread_mutex_destroy(g_bowl);pthread_cond_destroy(g_cond);return 0; } 执行结果 改进如下 执行结果我们虽然解决了刚刚多个吃面线程乱吃的问题但是这里发现运行到最后程序不跑了 查看一下调用栈 这样的一个场景就是所有线程都进入了等待队列中但是没有人通知。说到底产生这样的问题是因为所有做面线程已经进入等待队列当中去了但是此时吃面进程欲要通知等待队列中的做面线程但是它有可能将吃面线程通知出来导致所有线程进入等待状态。 解决程序卡死方式我们想要解决这个问题就要让做面线程每次唤醒的都是吃面线程而吃面线程每次唤醒的都是做面线程。当然也可以用int pthread_cond_broadcast(pthread_cond_t *cond);//唤醒PCB等待队列中所有的线程但是种不推荐使用因为太浪费CPU资源 改进如下 执行结果 发现不会再卡死或者疯狂吃面或者疯狂做面的情况针对不同的资源可以设置不同的条件变量 条件变量常见问题 条件变量的等待接口第二个参数为什么会有互斥锁 当线程被放入等待队列后就不会再执行后续代码了也就不会再进行解锁了锁将永远被锁上其他线程就不会获得这把锁来进行操作所以我们要将互斥锁传入条件变量等待函数在该函数内部进行解锁操作让其他线程可以拿到锁 pthread_cond_wait的内部是针对互斥锁做了上什么操作先释放互斥锁还是先将线程放入到PCB等待队列 pthread_cond_wait在调用的时候先要将线程放入等待队列当中。然后再释放互斥锁。 我们假设是先解锁然后再让线程入等待队列这样的话有可能这个线程刚解锁就有其他线程拿到锁并且修改了临界区资源而此时的临界区资源被修改后恰好满足条件所以这个线程就唤醒等待队列当中等待资源的线程但是现在等待队列中还没有线程它唤醒之后也进入了等待队列进行等待此时当此线程进入等待队列当中时之前的线程也刚刚到等待队列那么此时两个线程就同时进到等待队列中了谁也出不去 线程被唤醒之后会执行什么代码为什么需要获取互斥锁 pthread_cond_wait在返回之前一定会在其内部进行加锁操作就是当一个线程在调用pthread_cond_wait函数进入等待队列中后然后被唤醒时一定会在pthread_cond_wait函数中执行加锁操作。 而在加锁操作时 抢到了pthread_cond_wait函数就真正的执行完毕函数返回。没抢到pthread_cond_wait函数就没有被真正的执行完成还处于函数内部抢锁的逻辑然后一直来进行抢锁操作。
http://www.zqtcl.cn/news/397847/

相关文章:

  • 怎样做网站外部样式wordpress爱找主题
  • 自己搭建服务器做网站要多久问答网站如何优化
  • 网站用的服务器小程序拉新项目
  • 建设银行 访问的网站出错珠宝类网站模板
  • 网站百度关键词排名软件xampp里wordpress安装教程
  • 杭州网站设计建立企业网站专业做电脑系统下载网站好
  • 哈尔滨建设网站成本网站建设无广告
  • 发布网站搭建教程云排名网站
  • 无锡大型网站建设房地产景区网站建设方案
  • 自学网站建设工资公众号怎么开通直播功能
  • 网站建设上市公司wordpress park主题
  • 百度网站建设一年多少钱奇艺广州网站建设 熊掌号
  • 建设网站怎么收费标准网站和自媒体都可以做
  • 网站自己怎么做无锡常规网络营销是什么
  • 活泼风格的网站crm免费客户管理系统
  • 网站系统发生错误百度seo灰色词排名代发
  • 免费做名片儿的网站wordpress grace6
  • 有关网站开发的创意四川工程造价信息网官网
  • 网站目录结构北京注册公司地址可以是住宅吗
  • 龙信建设集团网站傻瓜式建站软件下载
  • 在360做网站和百度做网站的区别什么是网站地址
  • 营销型的物流网站模板下载长江设计公司
  • 网站程序制作购买网站域名
  • 网站建设中html下载如何用社交网站开发客户
  • 开设购物网站的方案政务公开和网站建设情况
  • 一台云服务器做多个网站营销型网站的建设重点是什么
  • 泉港网站建设推广服务公司电子商务好就业吗
  • 自己做网站开发如何找客户wordpress 显示 子分类
  • 腾讯邮箱网页版登录宿迁seo公司
  • 网站建设找盖亚科技WordPress 百度 主动