欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Pthread?并發(fā)編程線程自底向上深入解析

 更新時(shí)間:2022年11月16日 14:23:34   作者:一無是處的研究僧  
這篇文章主要為大家介紹了Pthread?并發(fā)編程線程自底向上深入解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

在本篇文章當(dāng)中主要給大家介紹線程最基本的組成元素,以及在 pthread 當(dāng)中給我們提供的一些線程的基本機(jī)制,因?yàn)楹芏嗾Z言的線程機(jī)制就是建立在 pthread 線程之上的,比如說 Python 和 Java,深入理解 pthread 的線程實(shí)現(xiàn)機(jī)制,可以極大的提升我們對(duì)于語言線程的認(rèn)識(shí)。希望能夠幫助大家深入理解線程。

線程的基本元素

首先我們需要了解一些我們?cè)谑褂镁€程的時(shí)候的常用的基本操作,如果不是很了解沒有關(guān)系我們?cè)诤罄m(xù)的文章當(dāng)中會(huì)仔細(xì)談?wù)撨@些問題。

  • 線程的常見的基本操作:

    • 線程的創(chuàng)建。
    • 線程的終止。
    • 線程之間的同步。
    • 線程的調(diào)度。
    • 線程當(dāng)中的數(shù)據(jù)管理。
    • 線程與進(jìn)程之間的交互。
  • 在 linux 當(dāng)中所有的線程和進(jìn)程共享一個(gè)地址空間。

  • 進(jìn)程與線程之間共享一些內(nèi)核數(shù)據(jù)結(jié)構(gòu):

    • 打開的文件描述符。
    • 當(dāng)前工作目錄。
    • 用戶 id 和用戶組 id 。
    • 全局?jǐn)?shù)據(jù)段的數(shù)據(jù)。
    • 進(jìn)程的代碼。
    • 信號(hào)(signals)和信號(hào)處理函數(shù)(signal handlers)。
  • 線程獨(dú)有的:

    • 線程的 ID 。
    • 寄存器線程和??臻g。
    • 線程的棧當(dāng)中的局部變量和返回地址。
    • 信號(hào)掩碼。
    • 線程自己的優(yōu)先級(jí)。
    • errno。

在所有的 pthread 的接口當(dāng)中,只有當(dāng)函數(shù)的返回值是 0 的時(shí)候表示調(diào)用成功。

線程等待

在 pthread 的實(shí)現(xiàn)當(dāng)中,每個(gè)線程都兩個(gè)特性:joinable 和 detached,當(dāng)我們啟動(dòng)一個(gè)線程的時(shí)候 (pthread_create) 線程的默認(rèn)屬性是 joinable,所謂 joinable 是表示線程是可以使用 pthread_join 進(jìn)行同步的。

當(dāng)一個(gè)線程調(diào)用 pthread_join(T, ret),當(dāng)這個(gè)函數(shù)返回的時(shí)候就表示線程 T 已經(jīng)終止了,執(zhí)行完成。那么就可以釋放與線程 T 的相關(guān)的系統(tǒng)資源。

如果一個(gè)線程的狀態(tài)是 detached 狀態(tài)的話,當(dāng)線程結(jié)束的時(shí)候與這個(gè)線程相關(guān)的資源會(huì)被自動(dòng)釋放掉,將資源歸還給系統(tǒng),也就不需要其他的線程調(diào)用 pthread_join 來釋放線程的資源。

pthread_join 函數(shù)簽名如下:

int pthread_join(pthread_t thread, void **retval);
  • thread 表示等待的線程。
  • retval 如果 retval 不等于 NULL 則在 pthread_join 函數(shù)內(nèi)部會(huì)將線程 thead 的退出狀態(tài)拷貝到 retval 指向的地址。如果線程被取消了,那么 PTHREAD_CANCELED 將會(huì)被放在 retval 指向的地址。
  • 函數(shù)的返回值
    • EDEADLK 表示檢測到死鎖了,比入兩個(gè)線程都調(diào)用 pthread_join 函數(shù)等待對(duì)方執(zhí)行完成。
    • EINVAL 線程不是一個(gè) joinable 的線程,一種常見的情況就是 pthread_join 一個(gè) detached 線程。
    • EINVAL 當(dāng)調(diào)用 pthrea_join 等待的線程正在被別的線程調(diào)用 pthread_join 等待。
    • ESRCH 如果參數(shù) thread 是一個(gè)無效的線程,比如沒有使用 pthread_create 進(jìn)行創(chuàng)建。
    • 0 表示函數(shù)調(diào)用成功。

在下面的程序當(dāng)中我們使用 pthread_join 函數(shù)去等待一個(gè) detached 線程:

#include <stdio.h>
#include <error.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
pthread_t t1, t2;
void* thread_1(void* arg) {
  int ret = pthread_detach(pthread_self());
  sleep(2);
  if(ret != 0)
    perror("");
  return NULL;
}
int main() {
  pthread_create(&t1, NULL, thread_1, NULL);
  sleep(1);
  int ret = pthread_join(t1, NULL);
  if(ret == ESRCH)
    printf("No thread with the ID thread could be found.\n");
  else if(ret == EINVAL) {
    printf("thread is not a joinable thread or Another thread is already waiting to join with this thread\n");
  }
  return 0;
}

上面的程序的輸出結(jié)果如下所示:

$ ./join.out
thread is not a joinable thread or Another thread is already waiting to join with this thread

在上面的程序當(dāng)中我們?cè)谝粋€(gè) detached 狀態(tài)的線程上使用 pthread_join 函數(shù),因此函數(shù)的返回值是 EINVAL 表示線程不是一個(gè) joinable 的線程。

在上面的程序當(dāng)中 pthread_self() 返回當(dāng)前正在執(zhí)行的線程,返回的數(shù)據(jù)類型是 pthread_t ,函數(shù) pthread_detach(thread) 的主要作用是將傳入的線程 thread 的狀態(tài)變成 detached 狀態(tài)。

我們?cè)賮砜匆粋€(gè)錯(cuò)誤的例子,我們?cè)谝粋€(gè)無效的線程上調(diào)用 pthread_join 函數(shù)

#include <stdio.h>
#include <error.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
pthread_t t1, t2;
void* thread_1(void* arg) {
  int ret = pthread_detach(pthread_self());
  sleep(2);
  if(ret != 0)
    perror("");
  return NULL;
}
int main() {
  pthread_create(&t1, NULL, thread_1, NULL);
  sleep(1);
  int ret = pthread_join(t2, NULL);
  if(ret == ESRCH)
    printf("No thread with the ID thread could be found.\n");
  else if(ret == EINVAL) {
    printf("thread is not a joinable thread or Another thread is already waiting to join with this thread\n");
  }
  return 0;
}

上面的程序的輸出結(jié)果如下:

$./oin01.out
No thread with the ID thread could be found.

在上面的程序當(dāng)中我們并沒有使用 t2 創(chuàng)建一個(gè)線程但是在主線程執(zhí)行的代碼當(dāng)中,我們使用 pthread_join 去等待他,因此函數(shù)的返回值是一個(gè) EINVAL 。

我們?cè)賮砜匆粋€(gè)使用 retval 例子:

#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
void* func(void* arg)
{
  pthread_exit((void*)100);
  return NULL;
}
int main() {
  pthread_t t;
  pthread_create(&t, NULL, func, NULL);
  void* ret;
  pthread_join(t, &ret);
  printf("ret = %ld\n", (u_int64_t)(ret));
  return 0;
}

上面的程序的輸出結(jié)果如下所示:

$./understandthread/join03.out
ret = 100

在上面的程序當(dāng)中我們使用一個(gè)參數(shù) ret 去獲取線程的退出碼,從上面的結(jié)果我們可以知道,我們得到了正確的結(jié)果。

如果我們沒有在線程執(zhí)行的函數(shù)當(dāng)中使用 pthread_exit 函數(shù)當(dāng)中明確的指出線程的退出碼,線程的退出碼就是函數(shù)的返回值。比如下面的的程序:

#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
void* func(void* arg)
{
  return (void*)100;
}
int main() {
  pthread_t t;
  pthread_create(&t, NULL, func, NULL);
  void* ret;
  pthread_join(t, &ret);
  printf("ret = %ld\n", (u_int64_t)(ret));
  return 0;
}

上面的程序的輸出結(jié)果也是 100 ,這與我們期待的結(jié)果是一致的。

獲取線程的棧幀和PC值

在多線程的程序當(dāng)中,每個(gè)線程擁有自己的棧幀和PC寄存器(執(zhí)行的代碼的位置,在 x86_86 里面就是 rip 寄存器的值)。在下面的程序當(dāng)中我們可以得到程序在執(zhí)行時(shí)候的三個(gè)寄存器 rsp, rbp, rip 的值,我們可以看到,兩個(gè)線程執(zhí)行時(shí)候的輸出是不一致的,這個(gè)也從側(cè)面反映出來線程是擁有自己的棧幀和PC值的。

#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
u_int64_t rsp;
u_int64_t rbp;
u_int64_t rip;
void find_rip() {
  asm volatile(
    "movq 8(%%rbp), %0;"
    :"=r"(rip)::
  );
}
void* func(void* arg) {
  printf("In func\n");
  asm volatile(             \
    "movq %%rsp, %0;"       \
    "movq %%rbp, %1;"       \
    :"=m"(rsp), "=m"(rbp):: \
  );
  find_rip();
  printf("stack frame: rsp = %p rbp = %p rip = %p\n", (void*)rsp, (void*)rbp, (void*) rip);
  return NULL;
}
int main() {
  printf("================\n");
  printf("In main\n");
  asm volatile(             \
    "movq %%rsp, %0;"       \
    "movq %%rbp, %1;"       \
    :"=m"(rsp), "=m"(rbp):: \
  );
  find_rip();
  printf("stack frame: rsp = %p rbp = %p rip = %p\n", (void*)rsp, (void*)rbp, (void*) rip);
  printf("================\n");
  pthread_t t;
  pthread_create(&t, NULL, func, NULL);
  pthread_join(t, NULL);
  return 0;
}

上面的程序的輸出結(jié)果如下所示:

================
In main
stack frame: rsp = 0x7ffc47096d50 rbp = 0x7ffc47096d80 rip = 0x4006c6
================
In func
stack frame: rsp = 0x7f0a60d43ee0 rbp = 0x7f0a60d43ef0 rip = 0x400634

從上面的結(jié)果來看主線程和線程 t 執(zhí)行的是不同的函數(shù),而且兩個(gè)函數(shù)的棧幀差距還是很大的,我們計(jì)算一下 0x7ffc47096d80 - 0x7f0a60d43ef0 = 1038949363344 = 968G 的內(nèi)存,因此很明顯這兩個(gè)線程使用的是不同的棧幀。

線程的線程號(hào)

在 pthread 當(dāng)中的一個(gè)線程對(duì)應(yīng)一個(gè)內(nèi)核的線程,內(nèi)核和 pthread 都給線程維護(hù)了一個(gè)線程的 id 號(hào),我們可以使用 gettid 獲取操作系統(tǒng)給我們維護(hù)的線程號(hào),使用函數(shù) pthread_self 得到 pthread 線程庫給我們維護(hù)的線程號(hào)!

#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
void* func(void* arg) {
  printf("pthread id = %ld tid = %d\n", pthread_self(), (int)gettid());
  return NULL;
}
int main() {
  pthread_t t;
  pthread_create(&t, NULL, func, NULL);
  pthread_join(t, NULL);
  return 0;
}

上面的程序的輸出結(jié)果如下

pthread id = 140063790135040 tid = 161643

線程與信號(hào)

在 pthread 庫當(dāng)中主要給我們提供了一些函數(shù)用于信號(hào)處理,我們?cè)?pthread 庫當(dāng)中可以通過函數(shù) pthread_kill 給其他的進(jìn)程發(fā)送信號(hào)。

 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX

我們可以在一個(gè)線程當(dāng)中響應(yīng)其他線程發(fā)送過來的信號(hào),并且響應(yīng)信號(hào)處理函數(shù),在使用具體的例子深入了解線程的信號(hào)機(jī)制之前,首先我們需要了解到的是在 pthread 多線程的程序當(dāng)中所有線程是共享信號(hào)處理函數(shù)的,如果在一個(gè)線程當(dāng)中修改了信號(hào)處理函數(shù),這個(gè)結(jié)果是會(huì)影響其他線程的。

#define _GNU_SOURCE
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
void sig(int signo) {
  char s[1024];
  sprintf(s, "signo = %d tid = %d pthread tid = %ld\n", signo, gettid(), pthread_self());
  write(STDOUT_FILENO, s, strlen(s));
}
void* func(void* arg) {
  printf("pthread tid = %ld\n", pthread_self());
  for(;;);
  return NULL;
}
int main() {
  signal(SIGHUP, sig);
  signal(SIGTERM, sig);
  signal(SIGSEGV, sig);
  pthread_t t;
  pthread_create(&t, NULL, func, NULL);
  sleep(1);
  pthread_kill(t, SIGHUP);
  sleep(1);
  return 0;
}

上面的程序的輸出結(jié)果如下所示:

pthread tid = 140571386894080
signo = 1 tid = 7785 pthread tid = 140571386894080

在上面的程序當(dāng)中,我們首先在主函數(shù)里面重新定義了幾個(gè)信號(hào)的處理函數(shù),將 SIGHUP、SIGTERM 和 SIGSEGV 信號(hào)的處理函數(shù)全部聲明為函數(shù) sig ,進(jìn)程當(dāng)中的線程接受到這個(gè)信號(hào)的時(shí)候就會(huì)調(diào)用對(duì)應(yīng)的處理函數(shù),在上面的程序當(dāng)中主線程會(huì)給線程 t 發(fā)送一個(gè) SIGHUP 信號(hào),根據(jù)前面信號(hào)和數(shù)據(jù)對(duì)應(yīng)關(guān)系我們可以知道 SIGHUP 對(duì)應(yīng)的信號(hào)的數(shù)字等于 1 ,我們?cè)谛盘?hào)處理函數(shù)當(dāng)中確實(shí)得到了這個(gè)信號(hào)。

除此之外我們還可以設(shè)置線程自己的信號(hào)掩碼,在前文當(dāng)中我們已經(jīng)提到了,每個(gè)線程都擁有線程自己的掩碼,因此在下面的程序當(dāng)中只有線程 2 響應(yīng)了主線程發(fā)送的 SIGTERM 信號(hào)。

#define _GNU_SOURCE
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
void sig(int signo) {
  char s[1024];
  sprintf(s, "signo = %d tid = %d pthread tid = %ld\n", signo, gettid(), pthread_self());
  write(STDOUT_FILENO, s, strlen(s));
}
void* func(void* arg) {
  sigset_t set;
  sigemptyset(&set);
  sigaddset(&set, SIGTERM);
  pthread_sigmask(SIG_BLOCK, &set, NULL);
  // 上面的代碼的功能是阻塞 SIGTERM 這個(gè)信號(hào) 當(dāng)這個(gè)信號(hào)傳輸過來的時(shí)候不會(huì)立即執(zhí)行信號(hào)處理函數(shù)
  // 而是會(huì)等到將這個(gè)信號(hào)變成非阻塞的時(shí)候才會(huì)響應(yīng)
  printf("func : pthread tid = %ld\n", pthread_self());
  for(;;);
  return NULL;
}
void* func02(void* arg) {
  printf("func02 : pthread tid = %ld\n", pthread_self());
  for(;;);
  return NULL;
}
int main() {
  signal(SIGTERM, sig);
  pthread_t t1;
  pthread_create(&t1, NULL, func, NULL);
  sleep(1);
  pthread_t t2;
  pthread_create(&t2, NULL, func02, NULL);
  sleep(1);
  pthread_kill(t1, SIGTERM);
  pthread_kill(t2, SIGTERM);
  sleep(2);
  return 0;
}

在上面的程序當(dāng)中我們創(chuàng)建了兩個(gè)線程并且定義了 SIGTERM 的信號(hào)處理函數(shù),在線程 1 執(zhí)行的函數(shù)當(dāng)中修改了自己阻塞的信號(hào)集,將 SIGTERM 變成了一種阻塞信號(hào),也就是說當(dāng)線程接受到 SIGTERM 的信號(hào)的時(shí)候不會(huì)立即調(diào)用 SIGTERM 的信號(hào)處理函數(shù),只有將這個(gè)信號(hào)變成非阻塞的時(shí)候才能夠響應(yīng)這個(gè)信號(hào),執(zhí)行對(duì)應(yīng)的信號(hào)處理函數(shù),但是線程 t2 并沒有阻塞信號(hào) SIGTERM ,因此線程 t2 會(huì)執(zhí)行對(duì)應(yīng)的信號(hào)處理函數(shù),上面的程序的輸出結(jié)果如下所示:

func : pthread tid = 139887896323840
func02 : pthread tid = 139887887931136
signo = 15 tid = 10652 pthread tid = 139887887931136

根據(jù)上面程序的輸出結(jié)果我們可以知道線程 t2 確實(shí)調(diào)用了信號(hào)處理函數(shù)(根據(jù) pthread tid )可以判斷,而線程 t1 沒有執(zhí)行信號(hào)處理函數(shù)。

在上文當(dāng)中我們還提到了在一個(gè)進(jìn)程當(dāng)中,所有的線程共享同一套信號(hào)處理函數(shù),如果在一個(gè)線程里面重新定義了一個(gè)信號(hào)的處理函數(shù),那么他將會(huì)影響其他的線程,比如下面的程序:

#define _GNU_SOURCE
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
void sig(int signo) {
  char s[1024];
  sprintf(s, "signo = %d tid = %d pthread tid = %ld\n", signo, gettid(), pthread_self());
  write(STDOUT_FILENO, s, strlen(s));
}
void sig2(int signo) {
  char* s = "thread-defined\n";
  write(STDOUT_FILENO, s, strlen(s));
}
void* func(void* arg) {
  signal(SIGSEGV, sig2);
  printf("pthread tid = %ld\n", pthread_self());
  for(;;);
  return NULL;
}
void* func02(void* arg) {
  printf("pthread tid = %ld\n", pthread_self());
  for(;;);
  return NULL;
}
int main() {
  signal(SIGSEGV, sig);
  pthread_t t;
  pthread_create(&t, NULL, func, NULL);
  sleep(1);
  pthread_t t2;
  pthread_create(&t2, NULL, func02, NULL);
  sleep(1);
  pthread_kill(t2, SIGSEGV);
  sleep(2);
  return 0;
}

上面的程序的輸出結(jié)果如下所示:

pthread tid = 140581246330624
pthread tid = 140581237937920
thread-defined

從上面程序輸出的結(jié)果我們可以看到線程 t2 執(zhí)行的信號(hào)處理函數(shù)是 sig2 而這個(gè)信號(hào)處理函數(shù)是在線程 t1 執(zhí)行的函數(shù) func 當(dāng)中進(jìn)行修改的,可以看到線程 t1 修改的結(jié)果確實(shí)得到了響應(yīng),從這一點(diǎn)也可以看出,如果一個(gè)線程修改信號(hào)處理函數(shù)是會(huì)影響到其他的線程的。

總結(jié)

在本篇文章當(dāng)中主要介紹了一些基礎(chǔ)了線程自己的特性,并且使用一些例子去驗(yàn)證了這些特性,幫助我們從根本上去理解線程,其實(shí)線程涉及的東西實(shí)在太多了,在本篇文章里面只是列舉其中的部分例子進(jìn)行使用說明,在后續(xù)的文章當(dāng)中我們會(huì)繼續(xù)深入的去談這些機(jī)制,比如線程的調(diào)度,線程的取消,線程之間的同步等等,更多關(guān)于Pthread 并發(fā)編程線程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C++ OpenCV實(shí)現(xiàn)抖音"藍(lán)線挑戰(zhàn)"特效

    C++ OpenCV實(shí)現(xiàn)抖音"藍(lán)線挑戰(zhàn)"特效

    這篇文章主要介紹了如何使用OpenCV C++ 實(shí)現(xiàn)抖音上的特效“藍(lán)線挑戰(zhàn)”。文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)OpenCV有一定的幫助,需要的可以參考一下
    2022-01-01
  • C語言創(chuàng)建windows窗口實(shí)例

    C語言創(chuàng)建windows窗口實(shí)例

    這篇文章主要介紹了C語言創(chuàng)建windows窗口實(shí)例,本文直接給出實(shí)現(xiàn)代碼,同時(shí)講解了編碼的步驟,需要的朋友可以參考下
    2015-04-04
  • C語言版五子棋游戲的實(shí)現(xiàn)代碼

    C語言版五子棋游戲的實(shí)現(xiàn)代碼

    這篇文章主要為大家詳細(xì)介紹了C語言版五子棋游戲的實(shí)現(xiàn)代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • 深入解析C++中類的多重繼承

    深入解析C++中類的多重繼承

    這篇文章主要介紹了深入解析C++中類的多重繼承,包括多重繼承相關(guān)的二義性問題,需要的朋友可以參考下
    2015-09-09
  • C++實(shí)現(xiàn)LeetCode(44.外卡匹配)

    C++實(shí)現(xiàn)LeetCode(44.外卡匹配)

    這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(44.外卡匹配),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • 詳解C語言中accept()函數(shù)和shutdown()函數(shù)的使用

    詳解C語言中accept()函數(shù)和shutdown()函數(shù)的使用

    這篇文章主要介紹了詳解C語言中accept()函數(shù)和shutdown()函數(shù)的使用,用來操作socket相關(guān)的網(wǎng)絡(luò)通信,需要的朋友可以參考下
    2015-09-09
  • C++高級(jí)程序員成長之路

    C++高級(jí)程序員成長之路

    本文主要是結(jié)合自身的經(jīng)驗(yàn),給大家羅列了一些如何成為C++高級(jí)程序員的方法和注意事項(xiàng),分享給大家,希望對(duì)新手能夠有所幫助。
    2015-12-12
  • vscode配置gitbash終端的方法步驟

    vscode配置gitbash終端的方法步驟

    本文主要介紹了vscode配置gitbash終端的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • C語言中數(shù)組排序淺析

    C語言中數(shù)組排序淺析

    這篇文章主要為大家介紹了C語言算法練習(xí)中數(shù)組元素排序的四種類型,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C語言有一定幫助,需要的可以參考一下
    2022-12-12
  • OpenCV 視頻中火焰檢測識(shí)別實(shí)踐

    OpenCV 視頻中火焰檢測識(shí)別實(shí)踐

    本文主要介紹了OpenCV 視頻中火焰檢測識(shí)別,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09

最新評(píng)論