linux多线程编程——同步与互斥

一、 为什么要用多线程技术?

1、避免阻塞,大家知道,单个进程只有一个主线程,当主线程阻塞的时候,整个进程也就阻塞了,无法再去做其它的一些功能了。

2、避免CPU空转,应用程序经常会涉及到RPC,数据库访问,磁盘IO等操作,这些操作的速度比CPU慢很多,而在等待这些响应时,CPU却不能去处理新的请求,导致这种单线程的应用程序性能很差。

3、提升效率,一个进程要独立拥有4GB的虚拟地址空间,而多个线程可以共享同一地址空间,线程的切换比进程的切换要快得多。

二、  如何使用多线程技术进行编程?

下面给出个多线程程序,一个最简单的模拟售票系统,代码如下:

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <pthread.h>  
  3.   
  4. void *ticketsell1(void *);  
  5. void *ticketsell2(void *);  
  6. int tickets = 20;  
  7.   
  8. int main()  
  9. {  
  10.     pthread_t id1,id2;  
  11.     int error;  
  12.   
  13.     error = pthread_create(&id1, NULL, ticketsell1, NULL);  
  14.     if(error != 0)  
  15.     {  
  16.         printf("pthread is not created!\n");  
  17.         return -1;  
  18.     }  
  19.   
  20.     error = pthread_create(&id2, NULL, ticketsell2, NULL);  
  21.     if(error != 0)  
  22.     {  
  23.         printf("pthread is not created!\n");  
  24.         return -1;  
  25.     }  
  26.   
  27.     pthread_join(id1,NULL);  
  28.     pthread_join(id2,NULL);  
  29.       
  30.     return 0;  
  31. }  
  32.   
  33. void *ticketsell1(void *arg)  
  34. {  
  35.     while(1)  
  36.     {  
  37.         if(tickets > 0)  
  38.         {  
  39. //          usleep(1000);  
  40.             printf("ticketse1 sells ticket:%d\n",tickets--);  
  41.         }  
  42.         else  
  43.         {  
  44.             break;  
  45.         }  
  46.     }  
  47.     return (void *)0;  
  48. }  
  49.   
  50. void *ticketsell2(void *arg)  
  51. {  
  52.     while(1)  
  53.     {  
  54.         if(tickets > 0)  
  55.         {  
  56. //          usleep(1000);  
  57.             printf("ticketse2 sells ticket:%d\n",tickets--);  
  58.         }  
  59.         else  
  60.         {  
  61.             break;  
  62.         }  
  63.     }  
  64.   
  65.     return (void *)0;  
  66. }  

执行结果如下:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/mthread$ ./mthread1   
  2. ticketse2 sells ticket:20  
  3. ticketse2 sells ticket:19  
  4. ticketse2 sells ticket:18  
  5. ticketse2 sells ticket:17  
  6. ticketse2 sells ticket:16  
  7. ticketse2 sells ticket:15  
  8. ticketse2 sells ticket:14  
  9. ticketse2 sells ticket:13  
  10. ticketse2 sells ticket:12  
  11. ticketse2 sells ticket:11  
  12. ticketse2 sells ticket:10  
  13. ticketse2 sells ticket:9  
  14. ticketse2 sells ticket:8  
  15. ticketse2 sells ticket:7  
  16. ticketse2 sells ticket:6  
  17. ticketse2 sells ticket:4  
  18. ticketse2 sells ticket:3  
  19. ticketse2 sells ticket:2  
  20. ticketse2 sells ticket:1  
  21. ticketse1 sells ticket:5  

看到结果,我们发现时能正常卖票的,一部分连续是sel2,另一部分是ticketsel1;

此时,其实存在一个隐含的问题,就是线程间的切换,在单CPU系统中,CPU是有时间片时间,时间片到了,就要执行其它的线程,假设thread1执行到if里面,但在printf执行前发生了线程切换,那么会发生什么呢?我们在这里用usleep函数(放开程序中的usleep注释行)进行强制模拟切换;

我们看看结果:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/mthread$ gcc -o mthread1 mthread1.c -lpthread  
  2. fs@ubuntu:~/qiang/mthread$ ./mthread1   
  3. ticketse2 sells ticket:20  
  4. ticketse1 sells ticket:19  
  5. ticketse2 sells ticket:18  
  6. ticketse1 sells ticket:17  
  7. ticketse2 sells ticket:16  
  8. ticketse1 sells ticket:15  
  9. ticketse2 sells ticket:14  
  10. ticketse1 sells ticket:13  
  11. ticketse2 sells ticket:12  
  12. ticketse1 sells ticket:11  
  13. ticketse2 sells ticket:10  
  14. ticketse1 sells ticket:9  
  15. ticketse2 sells ticket:8  
  16. ticketse1 sells ticket:7  
  17. ticketse2 sells ticket:6  
  18. ticketse1 sells ticket:5  
  19. ticketse2 sells ticket:4  
  20. ticketse1 sells ticket:3  
  21. ticketse1 sells ticket:2  
  22. ticketse2 sells ticket:1  
  23. ticketse1 sells ticket:0  
  24. fs@ubuntu:~/qiang/mthread$   

运行程序发现竟然有0号票被卖出了,这显然是错误的!当thread1的if里面发生线程切换时,thread2得到运行,把最后一张票卖了,此时thread1恢复运行,结果卖出了0号票,这里我们需要的是火车票的票数数据对于所有线程而言是同步的,所以就要用到线程同步技术了。

 

三、  使用多线程的同步与互斥

1、多线程的同步方式有很多种,例如互斥锁,条件变量,信号量,读写锁。先看看互斥锁如何解决多线程之间的同步问题。程序用互斥锁后如下:

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <pthread.h>  
  3.   
  4. void *ticketsell1(void *);  
  5. void *ticketsell2(void *);  
  6. int tickets = 20;  
  7. pthread_mutex_t mutex;  
  8.   
  9. int main()  
  10. {  
  11.     pthread_t id1,id2;  
  12.     pthread_mutex_init(&mutex, NULL);//  
  13.     int error;  
  14.   
  15.     error = pthread_create(&id1, NULL, ticketsell1, NULL);  
  16.     if(error != 0)  
  17.     {  
  18.         printf("pthread is not created!\n");  
  19.         return -1;  
  20.     }  
  21.   
  22.     error = pthread_create(&id2, NULL, ticketsell2, NULL);  
  23.     if(error != 0)  
  24.     {  
  25.         printf("pthread is not created!\n");  
  26.         return -1;  
  27.     }  
  28.   
  29.     pthread_join(id1,NULL);  
  30.     pthread_join(id2,NULL);  
  31.       
  32.     return 0;  
  33. }  
  34.   
  35. void *ticketsell1(void *arg)  
  36. {  
  37.     while(1)  
  38.     {  
  39.         pthread_mutex_lock(&mutex);//给互斥量上锁  
  40.         if(tickets > 0)  
  41.         {  
  42.             usleep(1000);  
  43.             printf("ticketse1 sells ticket:%d\n",tickets--);  
  44.             pthread_mutex_unlock(&mutex);//给互斥量解锁  
  45.               
  46.         }  
  47.         else  
  48.         {  
  49.             pthread_mutex_unlock(&mutex);//给互斥量解锁  
  50.             break;  
  51.         }  
  52.         pthread_yield();//线程调度函数,使每个线程都有执行机会  
  53.     }  
  54.     return (void *)0;  
  55. }  
  56.   
  57. void *ticketsell2(void *arg)  
  58. {  
  59.     while(1)  
  60.     {  
  61.         pthread_mutex_lock(&mutex);//给互斥量上锁  
  62.         if(tickets > 0)  
  63.         {  
  64.             usleep(1000);  
  65.             printf("ticketse2 sells ticket:%d\n",tickets--);  
  66.             pthread_mutex_unlock(&mutex);//给互斥量解锁  
  67.         }  
  68.         else  
  69.         {  
  70.             pthread_mutex_unlock(&mutex);//给互斥量解锁  
  71.             break;  
  72.         }  
  73.         pthread_yield();//线程调度函数,是两个线程都有执行机会  
  74.     }  
  75.   
  76.     return (void *)0;  
  77. }  

执行结果如下:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/mthread$ vi mthread1.c  
  2. fs@ubuntu:~/qiang/mthread$ gcc -o mthread1 mthread1.c -lpthread  
  3. fs@ubuntu:~/qiang/mthread$ ./mthread1   
  4. ticketse2 sells ticket:20  
  5. ticketse1 sells ticket:19  
  6. ticketse2 sells ticket:18  
  7. ticketse1 sells ticket:17  
  8. ticketse2 sells ticket:16  
  9. ticketse1 sells ticket:15  
  10. ticketse2 sells ticket:14  
  11. ticketse1 sells ticket:13  
  12. ticketse2 sells ticket:12  
  13. ticketse1 sells ticket:11  
  14. ticketse2 sells ticket:10  
  15. ticketse1 sells ticket:9  
  16. ticketse2 sells ticket:8  
  17. ticketse1 sells ticket:7  
  18. ticketse2 sells ticket:6  
  19. ticketse1 sells ticket:5  
  20. ticketse2 sells ticket:4  
  21. ticketse1 sells ticket:3  
  22. ticketse2 sells ticket:2  
  23. ticketse1 sells ticket:1  


2、再看看用信号量来解决多线程的同步问题,程序代码如下:

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <pthread.h>  
  3. #include <semaphore.h>  
  4.   
  5. void *ticketsell1(void *);  
  6. void *ticketsell2(void *);  
  7. int tickets = 20;  
  8. sem_t mutex,full;  
  9.   
  10. int main()  
  11. {  
  12.     pthread_t id1,id2;  
  13.     int error;  
  14.     int ret;  
  15.   
  16.     ret = sem_init(&mutex, 0 ,1);//初始化mutex信号量为1  
  17.     ret += sem_init(&full, 0 ,0);//初始化full信号量为0  
  18.   
  19.     if(ret != 0)  
  20.     {  
  21.         printf("sem_init fails!\n");  
  22.     }  
  23.   
  24.     error = pthread_create(&id1, NULL, ticketsell1, NULL);  
  25.     if(error != 0)  
  26.     {  
  27.         printf("pthread is not created!\n");  
  28.         return -1;  
  29.     }  
  30.   
  31.     error = pthread_create(&id2, NULL, ticketsell2, NULL);  
  32.     if(error != 0)  
  33.     {  
  34.         printf("pthread is not created!\n");  
  35.         return -1;  
  36.     }  
  37.   
  38.     pthread_join(id1,NULL);  
  39.     pthread_join(id2,NULL);  
  40.       
  41.     return 0;  
  42. }  
  43.   
  44. void *ticketsell1(void *arg)  
  45. {  
  46.     while(1)  
  47.     {  
  48.         sem_wait(&mutex);//mutex信号量进行P操作  
  49.         if(tickets > 0)  
  50.         {  
  51.             usleep(1000);  
  52.             printf("ticketse1 sells ticket:%d\n",tickets--);  
  53.             sem_post(&full);//full信号量进行V操作  
  54.         }  
  55.         else  
  56.         {  
  57.             sem_post(&full);//full信号量进行V操作  
  58.             break;  
  59.         }  
  60.     }  
  61.     return (void *)0;  
  62. }  
  63.   
  64. void *ticketsell2(void *arg)  
  65. {  
  66.     while(1)  
  67.     {  
  68.         sem_wait(&full);//full信号量进行P操作  
  69.         if(tickets > 0)  
  70.         {  
  71.             usleep(1000);  
  72.             printf("ticketse2 sells ticket:%d\n",tickets--);  
  73.             sem_post(&mutex);//mutex信号量进行V操作  
  74.         }  
  75.         else  
  76.         {  
  77.             sem_post(&mutex);//mutex信号量进行V操作  
  78.             break;  
  79.         }  
  80.     }  
  81.   
  82.     return (void *)0;  
  83. }  

执行结果:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/mthread$ vi mthread1.c  
  2. fs@ubuntu:~/qiang/mthread$ gcc -o mthread1 mthread1.c -lpthread  
  3. fs@ubuntu:~/qiang/mthread$ ./mthread1   
  4. ticketse1 sells ticket:20  
  5. ticketse2 sells ticket:19  
  6. ticketse1 sells ticket:18  
  7. ticketse2 sells ticket:17  
  8. ticketse1 sells ticket:16  
  9. ticketse2 sells ticket:15  
  10. ticketse1 sells ticket:14  
  11. ticketse2 sells ticket:13  
  12. ticketse1 sells ticket:12  
  13. ticketse2 sells ticket:11  
  14. ticketse1 sells ticket:10  
  15. ticketse2 sells ticket:9  
  16. ticketse1 sells ticket:8  
  17. ticketse2 sells ticket:7  
  18. ticketse1 sells ticket:6  
  19. ticketse2 sells ticket:5  
  20. ticketse1 sells ticket:4  
  21. ticketse2 sells ticket:3  
  22. ticketse1 sells ticket:2  
  23. ticketse2 sells ticket:1  
  24. fs@ubuntu:~/qiang/mthread$   

上面的sem_init函数用来初始化两个信号量的初始化值,这里一个设为1,一个设为0,sem_wait类似于P操作,让信号量减1,如果小于结果小于0,线程阻塞,否则线程继续执行,sem_post类似于V操作,提升信号量的值,加1,通过这两个信号量之间的互相“救对方”,就可以实现这两个线程的同步执行。

我们编译运行以上程序,发现两个售票点交替卖票,两个纯程依次得到机会执行,并且不会有0号票卖出,实现了同步。

 

3、我们再用条件变量来解决同步问题,一般条件变量需要结合互斥量一起使用,代码如下

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <pthread.h>  
  3. #include <semaphore.h>  
  4.   
  5. void *ticketsell1(void *);  
  6. void *ticketsell2(void *);  
  7. int tickets = 20;  
  8. pthread_mutex_t mutex;  
  9. pthread_cond_t  qready = PTHREAD_COND_INITIALIZER;//静态初始化条件变量;  
  10.           
  11. int main()  
  12. {  
  13.     pthread_t id1,id2;  
  14.     pthread_mutex_init(&mutex, NULL);  
  15.     int error;  
  16.   
  17.     error = pthread_create(&id1, NULL, ticketsell1, NULL);  
  18.     if(error != 0)  
  19.     {  
  20.         printf("pthread is not created!\n");  
  21.         return -1;  
  22.     }  
  23.   
  24.     error = pthread_create(&id2, NULL, ticketsell2, NULL);  
  25.     if(error != 0)  
  26.     {  
  27.         printf("pthread is not created!\n");  
  28.         return -1;  
  29.     }  
  30.   
  31.     pthread_join(id1,NULL);  
  32.     pthread_join(id2,NULL);  
  33.       
  34.     return 0;  
  35. }  
  36.   
  37. void *ticketsell1(void *arg)  
  38. {  
  39.     pthread_mutex_lock(&mutex);  
  40.     while(tickets > 0)  
  41.     {  
  42.         if(tickets%2 == 1)  
  43.         {  
  44.             usleep(1000);  
  45.             printf("ticketse1 sells ticket:%d\n",tickets--);  
  46.             pthread_cond_signal(&qready);//条件改变,发送信号,通知ticketse2  
  47.         }  
  48.         else  
  49.         {  
  50.             pthread_cond_wait(&qready,&mutex);//解开Mutex,并等待qready改变  
  51.         }  
  52.         pthread_mutex_unlock(&mutex);//给互斥量解锁  
  53.     }  
  54.     return (void *)0;  
  55. }  
  56.   
  57. void *ticketsell2(void *arg)  
  58. {  
  59.     pthread_mutex_lock(&mutex);  
  60.     while(tickets > 0)  
  61.     {  
  62.         if(tickets%2 == 0)  
  63.         {  
  64.             usleep(1000);  
  65.             printf("ticketse2 sells ticket:%d\n",tickets--);  
  66.             pthread_cond_signal(&qready);//条件改变,发送信号,通知ticketse1  
  67.         }  
  68.         else  
  69.         {  
  70.             pthread_cond_wait(&qready,&mutex);//解开mutex,并等待qready改变  
  71.         }  
  72.         pthread_mutex_unlock(&mutex);//给互斥量解锁  
  73.     }  
  74.   
  75.     return (void *)0;  
  76. }  

执行结果如下:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/mthread$ vi mthread1.c  
  2. fs@ubuntu:~/qiang/mthread$ gcc -o mthread1 mthread1.c -lpthread  
  3. fs@ubuntu:~/qiang/mthread$ ./mthread1   
  4. ticketse2 sells ticket:20  
  5. ticketse1 sells ticket:19  
  6. ticketse2 sells ticket:18  
  7. ticketse1 sells ticket:17  
  8. ticketse2 sells ticket:16  
  9. ticketse1 sells ticket:15  
  10. ticketse2 sells ticket:14  
  11. ticketse1 sells ticket:13  
  12. ticketse2 sells ticket:12  
  13. ticketse1 sells ticket:11  
  14. ticketse2 sells ticket:10  
  15. ticketse1 sells ticket:9  
  16. ticketse2 sells ticket:8  
  17. ticketse1 sells ticket:7  
  18. ticketse2 sells ticket:6  
  19. ticketse1 sells ticket:5  
  20. ticketse2 sells ticket:4  
  21. ticketse1 sells ticket:3  
  22. ticketse2 sells ticket:2  
  23. ticketse1 sells ticket:1  
  24. fs@ubuntu:~/qiang/mthread$   


  条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件变量发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线程间的同步.

函数pthread_cond_wait使线程阻塞在一个条件变量上,而函数pthread_cond_signal是用来释放被阻塞在条件变量上的一个线程。但是要注意的是,条件变量只是起到阻塞和唤醒线程的作用,具体的判断条件还需用户给出,我这里给出的是tickets是否是偶数这个条件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402281.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

黑马程序员_泛型

--------------------ASP.NetAndroidIOS开发、.Net培训、期待与您交流&#xff01; -------------------- 1. 泛型 1.概述 泛型是为了解决了集合中存储对象安全问题&#xff0c;如果集合中存数了不同类型的对象&#xff0c;那么读取出来后&#xff0c;操作取出的对象以为不…

菜鸟成长记(十一)----- 操蛋的2016与未知的2017

现在已经2017.1.16号了&#xff0c;早就说着要写篇总结&#xff0c;骂骂特么操蛋的自己&#xff0c;当然这两三年来在这里骂的真特么不在少数了&#xff0c;但是都是特么一拖再拖&#xff0c;刚刚明明是在看TPO阅读的&#xff0c;但是特么实在是无法集中精神的看&#xff0c;作…

VS.NET版本与VC版本对应关系

vc6 -> vc6vs2003 -> vc7vs2005 -> vc8vs2008 -> vc9vs2010 -> vc10vs2012 -> vc11vs2013 -> vc12仅供参考&#xff01;

sql2008 获取表结构说明

SELECT 表名 case when a.colorder1 then d.name else end, 表说明 case when a.colorder1 then isnull(f.value,) else end, 字段序号 a.colorder, 字段名 a.name, 标识 case when COLUMNPROPERTY( a.id,a.name,IsIdentity)1 th…

Linux ALSA声卡驱动之四:Control设备的创建

声明&#xff1a;本博内容均由http://blog.csdn.net/droidphone原创&#xff0c;转载请注明出处&#xff0c;谢谢&#xff01; Control接口 Control接口主要让用户空间的应用程序&#xff08;alsa-lib&#xff09;可以访问和控制音频codec芯片中的多路开关&#xff0c;滑动控件…

【linux】信号量的值定义

参见文件&#xff1a;/usr/include/bits/signum.h /* Signal number definitions. Linux version.Copyright (C) 1995-2013 Free Software Foundation, Inc.This file is part of the GNU C Library.The GNU C Library is free software; you can redistribute it and/ormodi…

汇编学习笔记-序章

最近突然对汇编语言开始感兴趣&#xff0c;于是说干就干了。 之前也自学过一点汇编&#xff0c;是跟着王爽老师的《汇编语言(第3版) 》这本书学习的&#xff0c;已经是有5 6前年的样子了。当时觉得这本书写的非常通俗易懂是一本非常好的启蒙书籍&#xff0c;但是最近在翻阅的时…

jQuery 入门教程(5): 显示/隐藏内容

2019独角兽企业重金招聘Python工程师标准>>> jQuery的hide()和show()可以用来显示和隐藏内容。比如下面的例子&#xff1a;jQuery的hide()和show() 可以用来显示和隐藏内容。比如下面的例子&#xff1a; [html] view plain copy print ? <!doctype html> …

键盘键值表

键盘键值表 值 描述 0x1 鼠标左键 0x2 鼠标右键 0x3 CANCEL 键 0x4 鼠标中键 0x8 BACKSPACE 键 0x9 TAB 键 0xC CLEAR 键 0xD ENTER 键 0x10 SHIFT 键 0x11 CTRL 键 0x12 MENU 键 0x13 PAUSE 键 0x14 CAPS LOCK 键 0x1B ESC 键 0x20 SPACEBAR 键 0x21 PAGE UP 键 0x22 PAGE DOW…

Django QuerySet API文档

在查询时发生了什么(When QuerySets are evaluated) QuerySet 可以被构造&#xff0c;过滤&#xff0c;切片&#xff0c;做为参数传递&#xff0c;这些行为都不会对数据库进行操作。只要你查询的时候才真正的操作数据库。 下面的 QuerySet 行为会导致执行查询的操作&#xff1a…

Spring自动扫描配置及使用方法

2019独角兽企业重金招聘Python工程师标准>>> 首先&#xff0c;检查一下你lib下有没有 common-annotations.jar 这个jar包 没有的话要导入工程。 下一步配置spring的配置文件applicationContex.xml&#xff0c;加入命名空间 红色为需要添加的内容 <beans xmlns…

Linux下ln命令使用

n是linux中又一个非常重要命令&#xff0c;它的功能是为某一个文件在另外一个位置建立一个同步的链接.当我们需要在不同的目录&#xff0c;用到相同的文件时&#xff0c;我们不需要在每一个需要的目录下都放一个必须相同的文件&#xff0c;我们只要在某个固定的目录&#xff0c…

6面向对象的程序设计

ECMA_262把对象定义为&#xff1a;无序属性的集合&#xff0c;其属性可以包含基本值、对象或者函数。 6.1理解对象 特性&#xff08;attribute&#xff09;是内部值&#xff0c;描述了属性&#xff08;property&#xff09;的各种特性。ECMAScript中有两种属性&#xff1a;数据…

DPM 2012 SP1---安装并部署DPM 2012 SP1服务器

实验拓扑图&#xff1a;一、前提条件&#xff1a;需要在DPM2012 SP1服务器上完成以下操作&#xff1a;1.DPM服务器加入域&#xff08;使用域管理员登陆DPM2012 SP1服务器&#xff09;2.准备存储磁盘&#xff08;新添加一块硬盘&#xff09;3.关闭防火墙服务&#xff08;DPM服务…

Linux下tail命令使用

linux tail命令用途是依照要求将指定的文件的最后部分输出到标准设备&#xff0c;通常是终端&#xff0c;通俗讲来&#xff0c;就是把某个档案文件的最后几行显示到终端上&#xff0c;假设该档案有更新&#xff0c;tail会自己主动刷新&#xff0c;确保你看到最新的档案内容。 一…

城市编号(省和市)

备注&#xff1a;这里只是个人的观点&#xff0c;有的地方也是copy&#xff0c;多多指教&#xff0c;个人笔记&#xff0c;有侵犯你们版权的地方还望海涵&#xff01;&#xff01;&#xff01; "qq": [ { "describe_content": "省", "type&…

Linux下test命令使用

test命令格式&#xff1a; [cpp] view plain copy test condition 通常&#xff0c;在if-then-else语句中&#xff0c;用[]代替&#xff0c;即[ condition ]。注意&#xff1a;方括号两边都要用空格。 1、数值比较 比 较 描 述 ---------------------------------------…

用Mysql网页式管理工具安全地访问数据库的方法

2019独角兽企业重金招聘Python工程师标准>>> 用Mysql网页式管理工具安全地访问数据库的方法 在使用通达OA系统时很多用户需要借助Mysql网页式管理工具进入后台数据库去查看数据&#xff0c;进行一些相应的操作。但是大多数时候用户安装完该工具后都是直接进入后台数…

Shell 脚本编程之基础

最近闲着无聊&#xff0c;把笔记写在这吧 0x00 特殊符号和常见的运算符 <1>特殊符号 $*&#xff1a;命令行所有参数组成的字符串 $&#xff1a;命令行所有参数组成的字符串 $n&#xff1a;n位数字&#xff0c;$0表示命令名称&#xff0c;$1表示命令行第一个参数&#xff…