热门标签 | 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类型。


推荐阅读
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本题探讨如何通过最大流算法解决农场排水系统的设计问题。题目要求计算从水源点到汇合点的最大水流速率,使用经典的EK(Edmonds-Karp)和Dinic算法进行求解。 ... [详细]
  • 尽管深度学习带来了广泛的应用前景,其训练通常需要强大的计算资源。然而,并非所有开发者都能负担得起高性能服务器或专用硬件。本文探讨了如何在有限的硬件条件下(如ARM CPU)高效运行深度神经网络,特别是通过选择合适的工具和框架来加速模型推理。 ... [详细]
  • 本题探讨了在一个有向图中,如何根据特定规则将城市划分为若干个区域,使得每个区域内的城市之间能够相互到达,并且划分的区域数量最少。题目提供了时间限制和内存限制,要求在给定的城市和道路信息下,计算出最少需要划分的区域数量。 ... [详细]
  • 非公版RTX 3080显卡的革新与亮点
    本文深入探讨了图形显卡的进化历程,重点介绍了非公版RTX 3080显卡的技术特点和创新设计。 ... [详细]
  • 本文详细介绍如何使用arm-eabi-gdb调试Android平台上的C/C++程序。通过具体步骤和实用技巧,帮助开发者更高效地进行调试工作。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • PHP 5.2.5 安装与配置指南
    本文详细介绍了 PHP 5.2.5 的安装和配置步骤,帮助开发者解决常见的环境配置问题,特别是上传图片时遇到的错误。通过本教程,您可以顺利搭建并优化 PHP 运行环境。 ... [详细]
  • Hadoop入门与核心组件详解
    本文详细介绍了Hadoop的基础知识及其核心组件,包括HDFS、MapReduce和YARN。通过本文,读者可以全面了解Hadoop的生态系统及应用场景。 ... [详细]
  • 基于KVM的SRIOV直通配置及性能测试
    SRIOV介绍、VF直通配置,以及包转发率性能测试小慢哥的原创文章,欢迎转载目录?1.SRIOV介绍?2.环境说明?3.开启SRIOV?4.生成VF?5.VF ... [详细]
  • 本文详细探讨了HTML表单中GET和POST请求的区别,包括它们的工作原理、数据传输方式、安全性及适用场景。同时,通过实例展示了如何在Servlet中处理这两种请求。 ... [详细]
  • 解决SVN图标显示异常问题的综合指南
    本文详细探讨了SVN图标无法正常显示的问题,并提供了多种有效的解决方案,涵盖不同环境下的具体操作步骤。通过本文,您将了解如何排查和修复这些常见的SVN图标显示故障。 ... [详细]
  • 利用决策树预测NBA比赛胜负的Python数据挖掘实践
    本文通过使用2013-14赛季NBA赛程与结果数据集以及2013年NBA排名数据,结合《Python数据挖掘入门与实践》一书中的方法,展示如何应用决策树算法进行比赛胜负预测。我们将详细讲解数据预处理、特征工程及模型评估等关键步骤。 ... [详细]
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
  • 本文详细介绍了如何使用ActionScript 3.0 (AS3) 连接并操作MySQL数据库。通过具体的代码示例和步骤说明,帮助开发者理解并实现这一过程。 ... [详细]
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社区 版权所有