热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Linux环境下多线程编程实战案例分析

在Linux环境下,多线程编程是实现高效并发处理的重要技术。本文通过具体的实战案例,详细分析了多线程编程的关键技术和常见问题。文章首先介绍了多线程的基本概念和创建方法,然后通过实例代码展示了如何使用pthreads库进行线程同步和通信。此外,还探讨了多线程程序中的性能优化技巧和调试方法,为开发者提供了宝贵的实践经验。

参考:

http://www.cnblogs.com/armlinux/archive/2010/05/28/2396997.html

http://blog.csdn.net/hitwengqi/article/details/8015646

http://www.vimer.cn/2009/11/linux%E4%B8%8B%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%9A%84%E5%88%9B%E5%BB%BA%E4%B8%8E%E7%AD%89%E5%BE%85%E8%AF%A6%E8%A7%A3.html

http://www.cnblogs.com/skynet/archive/2010/10/30/1865267.html

http://www.cnblogs.com/vamei/archive/2012/10/09/2715393.html



最近在优化一个图像处理算法,算法中要对于不同的图片做相同的图像处理算法,不同图片之间的处理数据时独立的,因而很自然的想到利用多线程优化算法。

下面是一些学习代码

一 Linux下面的多线编程需要包含明白以下几点:

1 pthread_t
       pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义:
  typedef unsigned long int pthread_t;
  它是一个线程的标识符。

2 pthread_create
      函数pthread_create用来创建一个线程,它的原型为:
      extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr,
  void *(*__start_routine) (void *), void *__arg));
       第一个参数为指向线程标识符的指针;

第二个参数用来设置线程属性;

第三个参数是线程运行函数的起始地址;

最后一个参数是运行函数的参数。

这里,我们的函数thread不需要参数,所以最后一个参数设为空指针。

第二个参数我们也设为空指针,这样将生成默认属性的线程。

对线程属性的设定和修改我们将在下一节 阐述。

当创建线程成功时,函数返回0,若不为0则说明创建线程失败,常见的错误返回代码为EAGAIN和EINVAL。

前者表示系统限制创建新的线程,例 如线程数目过多了;

后者表示第二个参数代表的线程属性值非法。

创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,

原来的线程则继续运行下一行 代码。

3 pthread_join 和pthread_exit
       函数pthread_join用来等待一个线程的结束。函数原型为:
  extern int pthread_join __P ((pthread_t __th, void **__thread_return));
       第一个参数为被等待的线程标识符;

第二个参数为一个用户定义的指针;

它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将 一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。一个线程的结束有两种途径,一种是像我们上面的例子一样,函数结束了,调用它的 线程也就结束了;

另一种方式是通过函数pthread_exit来实现。它的函数原型为:
  extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));
   唯一的参数是函数的返回代码,只要pthread_join中的第二个参数thread_return不是NULL,这个值将被传递给 thread_return。

最后要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线 程则返回错误代码ESRCH。
  在这一节里,我们编写了一个最简单的线程,并掌握了最常用的三个函数pthread_create,pthread_join和pthread_exit。

4 互斥锁相关
互斥锁用来保证一段时间内只有一个线程在执行一段代码。

pthread_mutex_init函数用来生成一个互斥锁。NULL参数表明使用默认属性。如果需要声明特定属性的互斥锁,须调用函数 pthread_mutexattr_init。函数pthread_mutexattr_setpshared和函数 pthread_mutexattr_settype用来设置互斥锁属性。前一个函数设置属性pshared,它有两个取值, PTHREAD_PROCESS_PRIVATE;

PTHREAD_PROCESS_SHARED;

前者用来不同进程中的线程同步,后者用于同步本进程的 不同线程。

在上面的例子中,我们使用的是默认属性PTHREAD_PROCESS_ PRIVATE。后者用来设置互斥锁类型,可选的类型有

PTHREAD_MUTEX_NORMAL、

PTHREAD_MUTEX_ERRORCHECK、

PTHREAD_MUTEX_RECURSIVE和

PTHREAD _MUTEX_DEFAULT。

它们分别定义了不同的上所、解锁机制,一般情况下,选用最后一个默认属性。
pthread_mutex_lock和 pthread_mutex_unlock以及pthread_delay_np
pthread_mutex_lock声明开始用互斥锁上锁,此后的代码直至调用pthread_mutex_unlock为止,均被上锁,即同一时间只 能被一个线程调用执行。当一个线程执行到pthread_mutex_lock处时,如果该锁此时被另一个线程使用,那此线程被阻塞,即程序将等待到另一 个线程释放此互斥锁。

 

二 简单的代码实例

 

[cpp] view plaincopy
  1. #include   
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6. using namespace std;  
  7.   
  8. pthread_t thread[2];  
  9. pthread_mutex_t mut;  
  10. double array[10][10];  
  11.   
  12. double funtime(const string &str)  
  13. {  
  14.     struct timeval tpstart,tpend;  
  15.     double timeuse;  
  16.     gettimeofday(&tpstart,NULL);  
  17.   
  18.     int loop = 1000000;  
  19.     while(loop--)  
  20.     {  
  21.         for (int i &#61; 0; i < 10; &#43;&#43;i)  
  22.         {  
  23.             for(int j &#61; 0; j < 10;&#43;&#43;j)  
  24.             {  
  25.                 array[i][j] &#61; 0.63;  
  26.                 array[i][j] /&#61; 0.96;  
  27.             }  
  28.         }  
  29.     }  
  30.     gettimeofday(&tpend,NULL);  
  31.     timeuse&#61;1000000*(tpend.tv_sec-tpstart.tv_sec)&#43;tpend.tv_usec-tpstart.tv_usec;  
  32.     timeuse/&#61;1000;  
  33.     cout << str << " timeuse: " << timeuse << " ms" << endl;  
  34.     return timeuse;  
  35. }  
  36.   
  37.   
  38. void *thread1(void *args)  
  39. {  
  40.     cout<< "thread1 : I&#39;m thread 1" << endl;  
  41.     funtime("thread1 fun");  
  42.     cout<< "thread1 :main function is waiting me" << endl;  
  43.     pthread_exit(NULL);  
  44. }  
  45.   
  46. void *thread2(void *args)  
  47. {  
  48.     cout<< "thread2 : I&#39;m thread 2" << endl;  
  49.     funtime("thread2 fun");  
  50.     cout<< "thread2 :main function is waiting me" << endl;  
  51.     pthread_exit(NULL);  
  52. }  
  53.   
  54. void thread_create(void)  
  55. {  
  56.     int temp;  
  57.     memset(&thread, 0, sizeof(thread));            
  58.     if((temp &#61; pthread_create(&thread[0], NULL, thread1, NULL)) !&#61; 0)       
  59.         cout<<"thread1 is created failed" << endl;  
  60.     else  
  61.         cout<<"thread1 is created " << endl;  
  62.     if((temp &#61; pthread_create(&thread[1], NULL, thread2, NULL)) !&#61; 0)    
  63.         cout<<"thread2 created failed" << endl;  
  64.     else  
  65.         cout<<"thread2 is created" << endl;  
  66. }  
  67.   
  68. void thread_wait(void)  
  69. {  
  70.     if(thread[0] !&#61;0)  
  71.     {       
  72.         pthread_join(thread[0],NULL);  
  73.         cout<<("thread1 is end \n");  
  74.     }  
  75.     if(thread[1] !&#61;0)   
  76.     {    
  77.         pthread_join(thread[1],NULL);  
  78.         cout<<("thread2 is end \n");  
  79.     }  
  80. }  
  81.   
  82.   
  83. int main()  
  84. {  
  85.     struct timeval tpstart,tpend;  
  86.     double timeuse;  
  87.     gettimeofday(&tpstart,NULL);  
  88.   
  89.     pthread_mutex_init(&mut,NULL);  
  90.     cout<<"main function is created thread" << endl;  
  91.     thread_create();  
  92.     cout<<"main function is waiting thread" << endl;  
  93.     thread_wait();  
  94.   
  95.     gettimeofday(&tpend,NULL);  
  96.     timeuse&#61;1000000*(tpend.tv_sec-tpstart.tv_sec)&#43;tpend.tv_usec-tpstart.tv_usec;  
  97.     timeuse/&#61;1000;  
  98.     cout << "MulThread " << " timeuse: " << timeuse << " ms" << endl;  
  99.   
  100.     cout << "signle thread time using: " <<   
  101.     funtime("main fun") &#43; funtime("main fun") << " ms" << endl;  
  102.   
  103.     return 0;  
  104. }  







测试运行&#xff1a;

linux 平台下面编译时需要加上 -lpthrad 如上面的代码test.cpp , 编译命令&#xff1a;

  g&#43;&#43;  -o test test.cpp -lpthread

可以看到两个线程都开始运行了&#xff0c;执行一次funtime函数需要花费1000ms左右的时间&#xff0c;单线程执行两次需要花费1953ms&#xff0c;而多线程利用两个线程并行执行花费的时间为1031ms&#xff0c;总的时间缩短了接近一倍。

为了验证多线程程序的执行结果是否正确&#xff0c;可以打印出矩阵观察。

修改代码&#xff1a;

[cpp] view plaincopy
  1. #include   
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6. using namespace std;  
  7.   
  8. pthread_t thread[2];  
  9. pthread_mutex_t mut;  
  10.   
  11. double array1[10][10];  
  12. double array2[10][10];  
  13. double array0[10][10];  
  14.   
  15. double funtime(const string &str, const int index)  
  16. {  
  17.     struct timeval tpstart,tpend;  
  18.     double timeuse;  
  19.     gettimeofday(&tpstart,NULL);  
  20.   
  21.     int loop &#61; 1000000;  
  22.     while(loop--)  
  23.     {  
  24.         for (int i &#61; 0; i < 10; &#43;&#43;i)  
  25.         {  
  26.             for(int j &#61; 0; j < 10;&#43;&#43;j)  
  27.             {  
  28.                 if (index &#61;&#61; 1)  
  29.                 {  
  30.                     array1[i][j] &#61; 1;  
  31.                     array1[i][j] /&#61; 2;  
  32.                 }else if(index &#61;&#61; 2)  
  33.                 {  
  34.                     array2[i][j] &#61; 2;  
  35.                     array2[i][j] /&#61; 2;  
  36.                 }else  
  37.                 {  
  38.                     array0[i][j] &#61; 3;  
  39.                     array0[i][j] /&#61; 2;  
  40.                 }  
  41.             }  
  42.         }  
  43.     }  
  44.   
  45.     gettimeofday(&tpend,NULL);  
  46.     timeuse&#61;1000000*(tpend.tv_sec-tpstart.tv_sec)&#43;tpend.tv_usec-tpstart.tv_usec;  
  47.     timeuse/&#61;1000;  
  48.     cout << str << " timeuse: " << timeuse << " ms" << endl;  
  49.     return timeuse;  
  50. }  
  51.   
  52.   
  53. void *thread1(void *args)  
  54. {  
  55.     cout<< "thread1 : I&#39;m thread 1" << endl;  
  56.     funtime("thread1 fun",1);  
  57.     cout<< "thread1 :main function is waiting me" << endl;  
  58.     pthread_exit(NULL);  
  59. }  
  60.   
  61. void *thread2(void *args)  
  62. {  
  63.     cout<< "thread2 : I&#39;m thread 2" << endl;  
  64.     funtime("thread2 fun",2);  
  65.     cout<< "thread2 :main function is waiting me" << endl;  
  66.     pthread_exit(NULL);  
  67. }  
  68.   
  69. void thread_create(void)  
  70. {  
  71.     int temp;  
  72.     memset(&thread, 0, sizeof(thread));            
  73.     if((temp &#61; pthread_create(&thread[0], NULL, thread1, NULL)) !&#61; 0)       
  74.         cout<<"thread1 is created failed" << endl;  
  75.     else  
  76.         cout<<"thread1 is created " << endl;  
  77.     if((temp &#61; pthread_create(&thread[1], NULL, thread2, NULL)) !&#61; 0)    
  78.         cout<<"thread2 created failed" << endl;  
  79.     else  
  80.         cout<<"thread2 is created" << endl;;  
  81. }  
  82.   
  83. void thread_wait(void)  
  84. {  
  85.     if(thread[0] !&#61;0)  
  86.     {       
  87.         pthread_join(thread[0],NULL);  
  88.         cout<<("thread1 is end \n");  
  89.     }  
  90.     if(thread[1] !&#61;0)   
  91.     {    
  92.         pthread_join(thread[1],NULL);  
  93.         cout<<("thread2 is end \n");  
  94.     }  
  95. }  
  96.   
  97.   
  98. void printresult()  
  99. {  
  100.     cout << "array1" << endl;  
  101.     for (int i &#61; 0; i < 10; &#43;&#43;i)  
  102.     {  
  103.         for (int j &#61; 0; j < 10; &#43;&#43;j)  
  104.         {  
  105.             cout << " " << array1[i][j];  
  106.         }  
  107.         cout << endl;  
  108.     }  
  109.   
  110.     cout <<"array2" <
  111.     for (int i &#61; 0; i < 10; &#43;&#43;i)  
  112.     {  
  113.         for (int j &#61; 0; j < 10; &#43;&#43;j)  
  114.         {  
  115.             cout << " " << array2[i][j];  
  116.         }  
  117.         cout << endl;  
  118.     }  
  119.   
  120.     cout <<"array0" <
  121.     for (int i &#61; 0; i < 10; &#43;&#43;i)  
  122.     {  
  123.         for (int j &#61; 0; j < 10; &#43;&#43;j)  
  124.         {  
  125.             cout << " " << array0[i][j];  
  126.         }  
  127.         cout << endl;  
  128.     }  
  129.   
  130. }  
  131.   
  132.   
  133. int main()  
  134. {  
  135.     struct timeval tpstart,tpend;  
  136.     double timeuse;  
  137.     gettimeofday(&tpstart,NULL);  
  138.   
  139.     pthread_mutex_init(&mut,NULL);  
  140.     cout<<"main function is created thread" << endl;  
  141.     thread_create();  
  142.     cout<<"main function is waiting thread" << endl;  
  143.     thread_wait();  
  144.   
  145.     gettimeofday(&tpend,NULL);  
  146.     timeuse&#61;1000000*(tpend.tv_sec-tpstart.tv_sec)&#43;tpend.tv_usec-tpstart.tv_usec;  
  147.     timeuse/&#61;1000;  
  148.     cout << "MulThread " << " timeuse: " << timeuse << " ms" << endl;  
  149.   
  150.     cout << "signle thread time using: " <<   
  151.     funtime("main fun",0) &#43; funtime("main fun",0) << " ms" << endl;  
  152.   
  153.     printresult();  
  154.   
  155.     return 0;  
  156. }  



从中可以看出线程执行结果是正确的

三 线程函数参数传递

funtime("thread1 fun",1);

funtime函数为线程调用中的主要执行函数&#xff0c;包含两个参数&#xff0c;都被写死了&#xff0c;实际中会有很多限制&#xff0c;因此需要考虑从线程的入口处传入参数。

前面的代码中&#xff0c;pthread_create(&thread[0], NULL, thread1, NULL)) !&#61; 0)中&#xff0c;第四个参数是线程执行函数需要传入的参数&#xff0c;在线程函数中void *thread1(void *args)中&#xff0c;只能允许传入一个参数&#xff0c;如果要传入多个参数&#xff0c;需要使用结构体&#xff0c;将多个参数组合为一个结构体一起传入。实例如下&#xff1a;

代码如下

[cpp] view plaincopy
  1. #include   
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6. using namespace std;  
  7.   
  8. //thread function pamter  
  9. struct theradPamter{  
  10.     int threadindex;  
  11.     string str;  
  12. };  
  13.   
  14. pthread_t thread[2];  
  15. pthread_mutex_t mut;  
  16.   
  17. double array1[10][10];  
  18. double array2[10][10];  
  19. double array0[10][10];  
  20.   
  21.   
  22. double funtime(const string &str, const int index)  
  23. {  
  24.     struct timeval tpstart,tpend;  
  25.     double timeuse;  
  26.     gettimeofday(&tpstart,NULL);  
  27.   
  28.     int loop &#61; 1000000;  
  29.     while(loop--)  
  30.     {  
  31.         for (int i &#61; 0; i < 10; &#43;&#43;i)  
  32.         {  
  33.             for(int j &#61; 0; j < 10;&#43;&#43;j)  
  34.             {  
  35.                 if (index &#61;&#61; 1)  
  36.                 {  
  37.                     array1[i][j] &#61; 1;  
  38.                     array1[i][j] /&#61; 2;  
  39.                 }else if(index &#61;&#61; 2)  
  40.                 {  
  41.                     array2[i][j] &#61; 2;  
  42.                     array2[i][j] /&#61; 2;  
  43.                 }else  
  44.                 {  
  45.                     array0[i][j] &#61; 3;  
  46.                     array0[i][j] /&#61; 2;  
  47.                 }  
  48.             }  
  49.         }  
  50.     }  
  51.   
  52.     gettimeofday(&tpend,NULL);  
  53.     timeuse&#61;1000000*(tpend.tv_sec-tpstart.tv_sec)&#43;tpend.tv_usec-tpstart.tv_usec;  
  54.     timeuse/&#61;1000;  
  55.     cout << str << " timeuse: " << timeuse << " ms" << endl;  
  56.     return timeuse;  
  57. }  
  58.   
  59.   
  60. void *thread1(void *args)  
  61. {  
  62.     cout<< "thread1 : I&#39;m thread 1" << endl;  
  63.     theradPamter *tp &#61; (theradPamter *) args;  
  64.     funtime(tp->str, tp->threadindex);  
  65.     cout<< "thread1 :main function is waiting me" << endl;  
  66.     pthread_exit(NULL);  
  67. }  
  68.   
  69. void *thread2(void *args)  
  70. {  
  71.     cout<< "thread2 : I&#39;m thread 2" << endl;  
  72.     theradPamter *tp &#61; (theradPamter *) args;  
  73.     funtime(tp->str, tp->threadindex);  
  74.     cout<< "thread2 :main function is waiting me" << endl;  
  75.     pthread_exit(NULL);  
  76. }  
  77.   
  78. void thread_create(theradPamter *tp1, theradPamter *tp2)  
  79. {  
  80.     int temp;  
  81.     memset(&thread, 0, sizeof(thread));  
  82.       
  83.     if((temp &#61; pthread_create(&thread[0], NULL, thread1, (void *)tp1)) !&#61; 0)       
  84.         cout<<"thread1 is created failed" << endl;  
  85.     else  
  86.         cout<<"thread1 is created " << endl;  
  87.     if((temp &#61; pthread_create(&thread[1], NULL, thread2, (void *)tp2)) !&#61; 0)    
  88.         cout<<"thread2 created failed" << endl;  
  89.     else  
  90.         cout<<"thread2 is created" << endl;  
  91.   
  92. }  
  93.   
  94. void thread_wait(void)  
  95. {  
  96.     if(thread[0] !&#61;0)  
  97.     {       
  98.         pthread_join(thread[0],NULL);  
  99.         cout<<("thread1 is end \n");  
  100.     }  
  101.     if(thread[1] !&#61;0)   
  102.     {    
  103.         pthread_join(thread[1],NULL);  
  104.         cout<<("thread2 is end \n");  
  105.     }  
  106. }  
  107.   
  108.   
  109. void printresult()  
  110. {  
  111.     cout << "array1" << endl;  
  112.     for (int i &#61; 0; i < 10; &#43;&#43;i)  
  113.     {  
  114.         for (int j &#61; 0; j < 10; &#43;&#43;j)  
  115.         {  
  116.             cout << " " << array1[i][j];  
  117.         }  
  118.         cout << endl;  
  119.     }  
  120.   
  121.     cout <<"array2" <
  122.     for (int i &#61; 0; i < 10; &#43;&#43;i)  
  123.     {  
  124.         for (int j &#61; 0; j < 10; &#43;&#43;j)  
  125.         {  
  126.             cout << " " << array2[i][j];  
  127.         }  
  128.         cout << endl;  
  129.     }  
  130.   
  131.     cout <<"array0" <
  132.     for (int i &#61; 0; i < 10; &#43;&#43;i)  
  133.     {  
  134.         for (int j &#61; 0; j < 10; &#43;&#43;j)  
  135.         {  
  136.             cout << " " << array0[i][j];  
  137.         }  
  138.         cout << endl;  
  139.     }  
  140.   
  141. }  
  142.   
  143.   
  144. int main()  
  145. {  
  146.     struct timeval tpstart,tpend;  
  147.     double timeuse;  
  148.     gettimeofday(&tpstart,NULL);  
  149.   
  150.     pthread_mutex_init(&mut,NULL);  
  151.     cout<<"main function is created thread" << endl;  
  152.   
  153.     theradPamter *tp1 &#61; new theradPamter;  
  154.     theradPamter *tp2 &#61; new theradPamter;    
  155.     tp1->threadindex &#61; 1;  
  156.     tp1->str &#61; "thread1 fun";    
  157.     tp2->threadindex &#61; 2;  
  158.     tp2->str &#61; "thread2 fun";    
  159.   
  160.     thread_create(tp1, tp2);  
  161.   
  162.     cout<<"main function is waiting thread" << endl;  
  163.     thread_wait();  
  164.   
  165.     delete tp1;  
  166.     tp1 &#61; NULL;  
  167.     delete tp2;  
  168.     tp2 &#61; NULL;  
  169.   
  170.     gettimeofday(&tpend,NULL);  
  171.     timeuse&#61;1000000*(tpend.tv_sec-tpstart.tv_sec)&#43;tpend.tv_usec-tpstart.tv_usec;  
  172.     timeuse/&#61;1000;  
  173.     cout << "MulThread " << " timeuse: " << timeuse << " ms" << endl;  
  174.   
  175.     cout << "signle thread time using: " <<   
  176.     funtime("main fun",0) &#43; funtime("main fun",0) << " ms" << endl;  
  177.   
  178.     printresult();  
  179.   
  180.     return 0;  
  181. }  



线程函数的参数需要传入一个void *类型的指针&#xff0c;因此需要在传递时&#xff0c;将tp指针强制转化为void*类型&#xff0c;传入 thread1(void *args) 中

在thread1(void *args)函数内&#xff0c;使用该指针时&#xff0c;在强制类型转换为threadPamter类型。


推荐阅读
  • 本题来自WC2014,题目编号为BZOJ3435、洛谷P3920和UOJ55。该问题描述了一棵不断生长的带权树及其节点上小精灵之间的友谊关系,要求实时计算每次新增节点后树上所有可能的朋友对数。 ... [详细]
  • JSOI2010 蔬菜庆典:树结构中的无限大权值问题
    本文探讨了 JSOI2010 的蔬菜庆典问题,主要关注如何处理非根非叶子节点的无限大权值情况。通过分析根节点及其子树的特性,提出了有效的解决方案,并详细解释了算法的实现过程。 ... [详细]
  • 本文介绍如何利用栈数据结构在C++中判断字符串中的括号是否匹配。通过顺序栈和链栈两种方式实现,并详细解释了算法的核心思想和具体实现步骤。 ... [详细]
  • 本文介绍了如何在 Node.js 中使用 `setDefaultEncoding` 方法为可写流设置默认编码,并提供了详细的语法说明和示例代码。 ... [详细]
  • 深入解析Java虚拟机(JVM)架构与原理
    本文旨在为读者提供对Java虚拟机(JVM)的全面理解,涵盖其主要组成部分、工作原理及其在不同平台上的实现。通过详细探讨JVM的结构和内部机制,帮助开发者更好地掌握Java编程的核心技术。 ... [详细]
  • 本文介绍 Java 中如何使用 Year 类的 atMonth 方法将年份和月份组合成 YearMonth 对象,并提供代码示例。 ... [详细]
  • 本文详细解释了为什么在成功执行移动赋值操作后,对象的析构函数会被调用,并提供了代码示例和详细的分析。 ... [详细]
  • Coursera ML 机器学习
    2019独角兽企业重金招聘Python工程师标准线性回归算法计算过程CostFunction梯度下降算法多变量回归![选择特征](https:static.oschina.n ... [详细]
  • Java 实现二维极点算法
    本文介绍了一种使用 Java 编程语言实现的二维极点算法。该算法用于从一组二维坐标中筛选出极点,适用于需要处理几何图形和空间数据的应用场景。文章不仅详细解释了算法的工作原理,还提供了完整的代码示例。 ... [详细]
  • 实用正则表达式有哪些
    小编给大家分享一下实用正则表达式有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下 ... [详细]
  • 本文介绍了如何使用JavaScript的Fetch API与Express服务器进行交互,涵盖了GET、POST、PUT和DELETE请求的实现,并展示了如何处理JSON响应。 ... [详细]
  • 本文探讨了如何在 F# Interactive (FSI) 中通过 AddPrinter 和 AddPrintTransformer 方法自定义类型(尤其是集合类型)的输出格式,提供了详细的指南和示例代码。 ... [详细]
  • 本文介绍如何从字符串中移除大写、小写、特殊、数字和非数字字符,并提供了多种编程语言的实现示例。 ... [详细]
  • 在高并发需求的C++项目中,我们最初选择了JsonCpp进行JSON解析和序列化。然而,在处理大数据量时,JsonCpp频繁抛出异常,尤其是在多线程环境下问题更为突出。通过分析发现,旧版本的JsonCpp存在多线程安全性和性能瓶颈。经过评估,我们最终选择了RapidJSON作为替代方案,并实现了显著的性能提升。 ... [详细]
  • 探讨ChatGPT在法律和版权方面的潜在风险及影响,分析其作为内容创造工具的合法性和合规性。 ... [详细]
author-avatar
请让我来打酱油
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有