轉載:多線程編程方法1-OpenMP框架

2021-03-01 SSD存儲知識匯總和分享

OpenMP支持c、cpp、fortran,本文對比使用openmp和未使用openmp的效率差距和外在表現,然後講解基礎知識。

一、舉例

1、使用OpenMP與未使用OpenMP的比較。

OpenMP是使用多線程的接口。

以c語言程序舉例,即ba.c文件如下:

#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
void Test(int n)
{
int j;
for (int i = 0; i < 100000000; ++i)
{
//do nothing, just waste time
j++;
}
printf("%d, ", n);
}
int main(int argc, char *argv[])
{
int i;

#pragma omp parallel for
for (i = 0; i < 100; ++i)
Test(i);

system("pause");
return 1;
}

在編譯時,參數如下:

編譯結果如下:

耗時:9s

注意:我的電腦為雙核,所以開啟了4個線程分別運行。

接下來,我通過window + R,輸入msconfig,並進入boot中的高級設置,將我電腦的設置為單核,然後再運行同樣的程序,可以發現結果如下:

於是可以發現,再設置為單核之後,程序會創建兩個線程,這樣的結果就是從0 50開始劃分,這樣顯然是沒有充分利用cpu的,所以將電腦設置為原來的雙核。

未優化的如下:

// #include <omp.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
void Test(int n)
{
int j;
for (int i = 0; i < 100000000; ++i)
{
//do nothing, just waste time
j++;
}
printf("%d, ", n);
}
int main(int argc, char *argv[])
{
int i;

// #pragma omp parallel for
for (i = 0; i < 100; ++i)
Test(i);

system("pause");
return 1;
}

在編譯時,參數如下:

編譯結果如下:

耗時:24s

不難得知,此程序使用的為單核單線程 ,所以運行速度遠遠低於使用多核多線程的速度。

上面輸出了100個數字,時間上來說優化後是未優化的3倍。

然後後續我又輸出了1000個數字,未優化的時間為240s,而優化後的時間為84s,可見優化後同樣也是優化前的3倍左右。

之所以四個線程的速度僅僅是單線程速度的3倍而不是4倍,這是因為多線程在線程的切換、合作等方面也需要花費一定的時間,所以只是到了3倍的差距,而沒有達到4倍的差距。

2、獲取當前線程id、獲取總的線程數

#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

int main(int argc, char *argv[])
{

int nthreads,tid;

// fork a team of thread
#pragma omp parallel private(nthreads,tid)
{
//obtian and print thread id
tid=omp_get_thread_num();
printf("Hello Word from OMP thread %d\n",tid);

// only master thread does this;
if(tid==0)
{
nthreads = omp_get_num_threads();
printf("Number of thread: %d\n",nthreads);
}
}

system("pause");
return 1;
}

編譯條件如下:

運行結果如下:

每次運行,可以發現順序是不同的。但是Number of thread: 4永遠是在線程0之後出現,並且tid==0時的這個線程為主線程。

3、之前使用的為c語言,下面改寫為c++。

#include <omp.h>
#include <iostream>
#include <windows.h>
using namespace std;
void Test(int n)
{
int j;
for (int i = 0; i < 100000000; ++i)
{
//do nothing, just waste time
j++;
}
cout << n << " ";
}
int main(int argc, char *argv[])
{
int i;

#pragma omp parallel for
for (i = 0; i < 100; ++i)
Test(i);

system("pause");
return 1;
}

編譯條件為 g++ t.cpp -o t -fopenmp,結果如下:

同樣地,這裡使用四核來生成的。

4、如下所示,也是使用c++語言。 

#include <iostream>
#include <windows.h>
#include <omp.h>
using namespace std;
void test(int m) {
int i = 0;
double a = 0.0;
double b = 0.0;
double c = 0.0;
for (i = 0; i < 100000000; i++) {
a += 0.1;
b += 0.2;
c = a + b;
}
cout << m << " ";
}
int main()
{
#pragma omp parallel for
for (int i = 0; i < 200; i++) {
test(i);
}
system("pause");
return 0;
}

在這裡也沒有什麼很大的區別,總之,我們就是需要將void test這個函數寫的複雜一些,然後就會耗時,這樣才能看出來變化。

另外,在這裡,我們可以看到結果中,是對for循環進行等量的劃分,比如對於i從0到200的for循環裡,會根據我電腦的2核劃分為0 - 50、 51-100、 101-150、 151-200這幾個區間,然後使用多核cpu進行運算,這個優化的效果我想是非常驚人的。

5、三層循環

#include <iostream>
#include <windows.h>
#include <omp.h>
using namespace std;
void test(int m, int n, int l) {
int i = 0;
double a = 0.0;
double b = 0.0;
double c = 0.0;
for (i = 0; i < 100000000; i++) {
a += 0.1;
b += 0.2;
c = a + b;
}
cout << m << " " << n << " " << l << endl;
}
int main()
{
cout << "first" << endl;
#pragma omp parallel for
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
for (int k = 0; k < 5; k++) {
test(i, j, k);
}
}
}

cout << "over hah" << endl;
cout << "over hah" << endl;
cout << "over hah" << endl;
cout << "over hah" << endl;
cout << "over hah" << endl;
cout << "over hah" << endl;
cout << "over hah" << endl;
cout << "over hah" << endl;
cout << "over hah" << endl;
cout << "over hah" << endl;
cout << "over hah" << endl;
cout << "over hah" << endl;
cout << "over hah" << endl;

cout << "second" << endl;
#pragma omp parallel for
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
for (int k = 0; k < 5; k++) {
test(i, j, k);
}
}
}
system("pause");
return 0;
}

結果,

可以看到,還是最外層的循環進行轉化。

耗時:45s。

如果我們將上面的兩個#pragma openmp parallel for去掉,再進行試驗。注意#pragma是預編譯指令,比如這裡告訴編譯器要進行並行運算。

則需要100s左右。雖然沒有特別明顯的提高,但是還是快了很多,優勢是非常明顯的。

上面的舉例都是一些簡單的例子,而對於具體的項目還會遇到問題,需要靈活應變。

二、基礎

  需要使用openmp就需要引入omp.h庫文件。然後在編譯時添加參數 -fopenmp即可。在具體需要進行並行運算的部分,使用 #pragma omp 指令[子句] 來告訴編譯器如何並行執行對應的語句。常用的指令如下:

parallel - 即#pragma omp parallel 後面需要有一個代碼片段,使用{}括起來,表示會被並行執行。

parallel for - 這裡後面跟for語句即可,不需要有額外的代碼塊。

sections

parallel sections

single - 表示只能單線程執行

critical - 臨界區,表示每次只能有一個openmp線程進入

barrier - 用於並行域內代碼的線程同步,線程執行到barrier時停下來 ,直到所有線程都執行到barrier時才繼續。

  常用的子句如下:

num_threads - 指定並行域內線程的數目

shared - 指定一個或者多個變量為多個線程的共享變量

private - 指定一個變量或者多個變量在每個線程中都有它的副本  

  另外,openmp還提供了一些列的api函數來獲取並行線程的狀態或控制並行線程的行為,常用api如下:

omp_in_parallel - 判斷當前是否在並行域中。

omp_get_thread_num - 獲取線程號

omp_set_num_threads - 設置並行域中線程格式

omp_get_num_threads - 返回並行域中線程數

omp_get_dynamic - 判斷是否支持動態改變線程數目

omp_get_max_threads - 獲取並行域中可用的最大的並行線程數目

omp_get_num_procs - 返回系統中處理器的個數  

1、如下使用parallel,會根據電腦配置並行執行多次。

#include <iostream>
#include <windows.h>
#include <omp.h>
using namespace std;
int main()
{
#pragma omp parallel
{
cout << "this is in parallel" << endl;
}
system("pause");
return 0;
}

 

2、使用parallel num_threads(3),限制並行的線程數為3。

#include <iostream>
#include <windows.h>
#include <omp.h>
using namespace std;
int main()
{
#pragma omp parallel num_threads(3)
{
cout << "this is in parallel" << endl;
}
system("pause");
return 0;
}

這樣,最終會輸出3個語句,因為語句被並行運行了3次。結果如下:

但是上面的結果不是固定的,這裡可以很明顯的表示出程序是並行運行的,因為第一個輸出還沒來得及換行,第二個又繼續輸出了,所以它們是獨立地並行地運算的。

3、下面我們使用 #pragma parallel for num_threads(4),並且在並行域中,我們還通過 omp_get_thread_num()來獲取線程號,如下:

#include <iostream>
#include <windows.h>
#include <omp.h>
using namespace std;
int main()
{
#pragma omp parallel for num_threads(4)
for (int i = 0; i < 20; i++) {
cout << omp_get_thread_num() << endl;
}
system("pause");
return 0;
}

這裡就是對這個for循環使用4個線程來並行。注意 #pragma omp parallel for num_threads(4) 與 #pragma omp parallel  num_threads(4) 不同,可自行體會。

結果如下,出現空行是因為多線程並行運算,導致換行符沒來得及輸出另外一個線程號就被輸出了。

4、對比單線程、2線程、4線程、. 、12線程效率。

#include <iostream>
#include <windows.h>
#include <omp.h>
using namespace std;
void test() {
int j = 0;
for (int i = 0; i < 100000; i++) {
// do something to kill time...
j++;
}
};

int main()
{

double startTime;
double endTime;

// 不使用openMp
startTime = omp_get_wtime();
for (int i = 0; i < 100000; i++) {
test();
}
endTime = omp_get_wtime();
cout << "single thread cost time: " << endTime - startTime << endl;

// 2個線程
startTime = omp_get_wtime();
#pragma omp parallel for num_threads(2)
for (int i = 0; i < 100000; i++) {
test();
}
endTime = omp_get_wtime();
cout << "2 threads cost time: " << endTime - startTime << endl;

// 4個線程
startTime = omp_get_wtime();
#pragma omp parallel for num_threads(4)
for (int i = 0; i < 100000; i++) {
test();
}
endTime = omp_get_wtime();
cout << "4 threads cost time: " << endTime - startTime << endl;

// 6個線程
startTime = omp_get_wtime();
#pragma omp parallel for num_threads(6)
for (int i = 0; i < 100000; i++) {
test();
}
endTime = omp_get_wtime();
cout << "6 threads cost time: " << endTime - startTime << endl;

// 8個線程
startTime = omp_get_wtime();
#pragma omp parallel for num_threads(8)
for (int i = 0; i < 100000; i++) {
test();
}
endTime = omp_get_wtime();
cout << "8 threads cost time: " << endTime - startTime << endl;

// 10個線程
startTime = omp_get_wtime();
#pragma omp parallel for num_threads(10)
for (int i = 0; i < 100000; i++) {
test();
}
endTime = omp_get_wtime();
cout << "10 threads cost time: " << endTime - startTime << endl;

// 12個線程
startTime = omp_get_wtime();
#pragma omp parallel for num_threads(12)
for (int i = 0; i < 100000; i++) {
test();
}
endTime = omp_get_wtime();
cout << "12 threads cost time: " << endTime - startTime << endl;

system("pause");
return 0;
}

結果如下:

 

 於是,我們可以看到,單線程(不使用openMP)時消耗時間最長,2線程約為單線程的一半,4個線程(本電腦為4個邏輯內核)約為1/3時間,6個線程的時候時間甚至更長,12個線程在時間上也沒有明顯額減少,所以,線程數的制定可以根據電腦的核心數來做出選擇。

更多例子:

#include <iostream>
#include <windows.h>
#include <omp.h>
using namespace std;
void test(int i) {
int j = 0;
for (int i = 0; i < 100000000; i++) {
// do something to kill time...
j++;
}
cout << i << endl;
};

int main()
{

// 此程序中使用到的openmp不可簡單地理解為一個封裝起來的庫,實際上應該理解為一個框架。這個框架是由硬體開發商和軟體開發商共同開發的,即通過協商api,來使得多核並行運算更容易上手、使用
// 主要參考文章:https://wdxtub.com/2016/03/20/openmp-guide/

// #pragma omp parallel for
// for (int i = 0; i != 10; i++) {
// test(i);
// }



// #pragma omp parallel
// {
// #pragma omp for
// for (int i = 0; i < 10; i++) {
// test(i);
// }
// }



// XXX 錯誤,對於並行運算,不支持 != 的形式
// #pragma omp parallel for
// for (int i = 0; i != 10; i++) {
// test(i);
// }



// 在並行區域內聲明了一個變量private_a,那麼在多線程執行時,每個線程都會創建這麼一個private_a變量。
// 最終輸出結果為668/668/669/669,說明2個線程加了兩次,2個線程加了3次。
// #pragma omp parallel
// {
// int private_a = 666;
// #pragma omp for
// for (int i = 0; i < 10; i++) {
// test(i);
// private_a++;
// }
// cout << private_a << endl;
// }



// 在並行區域之外定義的變量是共享的,即使下面有多個線程並行執行for循環,但是不會為每個線程創建share_a變量,所以最終每個線程訪問的都是同一個內存,輸出的結果為4個676
// int share_a = 666;
// #pragma omp parallel
// {
// #pragma omp for
// for (int i = 0; i < 10; i++) {
// test(i);
// share_a++;
// }
// cout << share_a << endl;
// }


// 注意:這種循環是普通的循環,其中的sum是共享的,然後sum是累加的,所以從結果中也可以看出sum一定是非遞減的,最終結果為45。
// int sum = 0;
// cout << "Before: " << sum << endl;
// #pragma omp parallel for
// for (int i = 0; i < 10; i++) {
// sum = sum + i;
// cout << sum << endl;
// }
// cout << "After: " << sum << endl;


// 注意:這裡採用了reduction(+:sum),所以每個線程根據reduction(+:sum)的聲明計算出自己的sum(注意:在每個線程計算之初,sum均為在並行域之外規定的0,即對於4個線程而言,4個線程都會有一個初始值為0的sum,然後再疊加),然後再將各個線程的sum添加起來,所以從結果來看,sum是不存在某種特定規律的。
// int sum = 0;
// cout << "Before: " << sum << endl;
// #pragma omp parallel for reduction(+:sum)
// for (int i = 0; i < 10; i++) {
// sum = sum + i;
// cout << sum << endl;
// }
// cout << "After: " << sum << endl;


// 下面的減法是類似的,對比上面的兩個例子即可。
// int sum = 100;
// cout << "Before: " << sum << endl;
// #pragma omp parallel for
// for (int i = 0; i < 10; i++) {
// sum = sum - i;
// cout << sum << endl;
// }
// cout << "After: " << sum << endl;

// int sum = 100;
// cout << "Before: " << sum << endl;
// #pragma omp parallel for reduction(-:sum)
// for (int i = 0; i < 10; i++) {
// sum = sum - i;
// cout << sum << endl;
// }
// cout << "After: " << sum << endl;



// 下面的兩個例子中一個使用了原子操作,一個沒有使用原子操作。
// 使用原子操作的最後結果正確且穩定,而沒有使用原子操作最終的結果是不穩定的。
// int sum = 0;
// cout << "Before: " << sum << endl;
// #pragma omp parallel for
// for (int i = 0; i < 20000; i++) {
// #pragma omp atomic
// sum++;
// }
// cout << "Atomic-After: " << sum << endl;

// int sum = 0;
// #pragma omp parallel for
// for (int i = 0; i < 20000; i++) {
// sum++;
// }
// cout << "None-atomic-After: " << sum << endl;



// 線程同步之critical
// 使用critical得到的結果是穩定的,而不使用critical得到的結果是不穩定的。
// 值得注意的是:critical與atomic的區別在於 - atomic僅僅使用自增(++、--等)或者簡化(+=、-=等)兩種方式,
// 並且只能表示下一句,而critical卻沒有限制,且可以通過{}代碼塊來表示多句同時只能有一個線程來訪問。
// int sum = 0;
// cout << "Before: " << sum << endl;
// #pragma omp parallel for
// for (int i = 0; i < 100; i++) {
// #pragma omp critical(a)
// {
// sum = sum + i;
// sum = sum + i * 2;
// }
// }
// cout << "After: " << sum << endl;


// 同時運行下面的兩個程序,可以發現有些許不同。
// 這個程序中的第一個for循環會多線程執行,並且如果一個線程執行完,如果有的線程沒有執行完,
// 那麼就會等到所有線程執行完了再繼續向下執行。所以結果中 - 和 + 區分清晰。
// #pragma omp parallel
// {
// #pragma omp for
// for (int j = 666; j < 1000; j++) {
// cout << "-" << endl;
// }

// #pragma omp for nowait
// for (int i = 0; i < 100; i++) {
// cout << "+" << endl;
// }
// }

// 這個程序中的第一個for循環同樣會有多個線程同時執行,只是其中某個線程最先執行完了之後,
// 不會等其他的線程,而是直接進入了下一個for循環,所以結果中的 - 和 + 在中間部分是混雜的。
// #pragma omp parallel
// {
// #pragma omp for nowait
// for (int i = 0; i < 100; i++) {
// cout << "+" << endl;
// }

// #pragma omp for
// for (int j = 666; j < 1000; j++) {
// cout << "-" << endl;
// }
// }
//
// 可知,barrier為隱式柵障,即並行區域中所有線程執行完畢之後,主線程才繼續執行。
// 而nowait的聲明即可取消柵障,這樣,即使並行區域內即使所有的線程還沒有執行完,
// 但是執行完了的線程也不必等待所有線程執行結束,而可自動向下執行。


// 如下所示正常來說應該是第一個for循環中的一個線程執行完之後nowait進入下一個for循環,
// 但是我們通過 #pragma omp barrier 來作為顯示同步柵障,即讓這個先執行完的線程等待所有線程執行完畢再進行下面的運算
// #pragma omp parallel
// {
// #pragma omp for nowait
// for (int i = 0; i < 100; i++) {
// cout << "+" << endl;
// }

// #pragma omp barrier

// #pragma omp for
// for (int j = 666; j < 1000; j++) {
// cout << "-" << endl;
// }
// }



// 這裡我們通過#pragma omp master來讓主線程執行for循環,然後其他的線程執行後面的cout語句,
// 所以,cout的內容會出現在for循環多次(這取決於你電腦的性能),最後,主線程執行完for語句後,也會執行一次cout
// #pragma omp parallel
// {
// #pragma omp master
// {
// for (int i = 0; i < 10; i++) {
// cout << i << endl;
// }
// }
// cout << "This will be shown two or more times" << endl;
// }



// 使用section可以指定不同的線程來執行不同的部分
// 如下所示,通過#pragma omp parallel sections來指定不同的section由不同的線程執行
// 最後得到的結果是多個for循環是混雜在一起的
// #pragma omp parallel sections
// {
// #pragma omp section
// for (int i = 0; i < 10; i++) {
// cout << "+";
// }

// #pragma omp section
// for (int j = 0; j < 10; j++) {
// cout << "-";
// }

// #pragma omp section
// for (int k = 0; k < 10; k++) {
// cout << "*";
// }
// }

system("pause");
return 0;
}

通過上面的例子,我們就可以對OpenMP有一個基本的入門過程了。

相關焦點

  • OpenMP編程初步(Fortran語言)
    在程序編寫的複雜程度上,OpenMP只需在串行程序上做少許的改動即可實現並行,而MPI的編程往往比較複雜,一般與串行程序是兩套不同的代碼。本文簡單介紹Fortran語言下使用OpenMP實現並行的方法。話不多說,直接上一個簡單的例子:在上述程序中,以!$omp開頭的語句稱為編譯指導語句,控制程序的並行。注意到,這些語句是以!
  • 轉載:多線程編程方法2-OpenCL框架
    1.OpenCL概念OpenCL是一個為異構平臺編寫程序的框架,此異構平臺可由CPU、GPU或其他類型的處理器組成。
  • 漫談分布式計算框架
    從這個 level 講,它們都定義了一種「分布式計算模型」,即提出了一種計算的方法,通過這種計算方法,就能夠解決大量數據的分布式計算問題。它們的區別在於提出的分布式計算模型不同。Mapreduce 正如其名,是一個很基本的 map-reduce 式的計算模型(好像沒說一樣)。Spark 定義了一套 RDD 模型,本質上是一系列的 map/reduce 組成的一個 DAG 圖。
  • JavaScript 多線程編程
    通過類似定時器,回調函數等異步編程方式在平常的工作中已經足夠,但是如果做複雜運算,這種方式的不足就逐漸體現出來,比如settimeout拿到的值並不正確,或者頁面有複雜運算的時候很容易觸發假死狀態,異步代碼會影響主線程的代碼執行,異步終究還是單線程,不能從根本上解決問題。
  • Python多線程編程技術總結
    學習並發編程程序運行的5種並發粒度怎樣選擇並發技術線程池和進程池原理:提前創建好線程/進程放在池子裡,新的task到來可以重用這些資源,減少了新建、終止線程/進程的開銷提升性能:因為減去了大量新建、終止線程的開銷,重用了線程資源適用場景:適合處理突發性大量請求或需要大量線程完成任務、但實際任務處理時間較短防禦功能:能有效避免系統因為創建線程過多,而導致系統負荷過大響應變慢等問題代碼簡潔
  • Leez P710開發板 OpenMP 並行替代方案
    當使用8個A53核心一起跑的時候,時間縮減到約1.63秒。對於8核A53,8線程並行可以使計算速度提高2.48倍。那麼換做是RK3399呢?與上面同樣的代碼,我們編譯並運行一下,如下圖:但是,當使用8線程跑的時候,耗時約1.00秒,與單線程時候相比只提速了8%。並行的速度提升幾乎可以忽略不計!我敏銳地覺察到,問題是出在A72核心上。於是,又寫了下面的代碼,我計劃對RK3399上的A72和A53核心的OpenMP並行性能進行單獨的測試。代碼如下:
  • 程序丨D3D11和D3D12多線程渲染框架的比較
    如果說D3D11中對多線程渲染的支持還只是停留在「小荷才露尖尖角」的話,那麼D3D12中的多線程渲染則是一番「接天蓮葉無窮碧,映日荷花別樣紅」的盛況了。本文就將從概念、編程原理、編程框架方面解剖一下D3D的多線程渲染技術。因本人知識水平有限,同時期待您的閱讀與批評指正!
  • 設置計算機-使用OpenMP 進行 Fortran95並行計算
    1、設置(1) 在vs2010 中新建fortran 控制臺程序,選擇項目
  • C++ Builder 多線程編程技術經驗談
    在此,本文簡要討論了C++Builder平臺下如何利用多線程編程技術實現」生產者-消費者」問題,幫助我們更好得理解同步概念及其實現方法。生產者-消費者問題生產者-消費者問題是一個著名的進程同步問題。它描述的是:有一群生產者進程在生產消息,並將此消息提供給消費者進程去消費。
  • java多線程實現TCP網絡Socket編程(C/S通信)
    本篇將解決這個問題,詳細記錄實現java多線程通信,目標是使客戶端可以一次接收伺服器發送的多條信息,避免阻塞。方法是將客戶端接收信息功能獨立為一個線程來完成,進一步完善TCP的Socket網絡通信,C/S軟體架構的程序設計!
  • Java多線程:線程的同步與鎖
    實質上,線程進入該對象的的一種池中,必須在哪裡等待,直到其鎖被釋放,該線程再次變為可運行或運行為止。當考慮阻塞時,一定要注意哪個對象正被用於鎖定:      1、調用同一個對象中非靜態同步方法的線程將彼此阻塞。如果是不同對象,則每個線程有自己的對象的鎖,線程間彼此互不幹預。
  • fork()函數與Linux中的多線程編程
    因為長期以來程序都是單線程的,fork()運轉正常。當20世紀90年代初期引入線程之後,fork()的適用範圍就大為縮小了。在多線程執行的情況下調用fork()函數,僅會將發起調用的線程複製到子進程中。(子進程中該線程的ID與父進程中發起fork()調用的線程ID是一樣的,因此,線程ID相同的情況有時我們需要做特殊的處理。)也就是說不能同時創建出於父進程一樣多線程的子進程。
  • 總結C++多線程系統編程精要
    學習多線程編程面臨的最大思維方式的轉變有兩點:當前線程可能被切換出去, 或者說被搶佔(preempt)了。
  • 漫道多線程(一):多線程與並行計算簡述
    = new MyThread("Thread1"); //通過從Thread類中所繼承的start()方法啟動線程; thread1.start(); } }實現Runable接口(java8的Lambda可以輕鬆實現)// 調用public class ThreadLearn2
  • 使用Python實現多線程和多處理方法
    在本教程中,我們將學習如何使用Python實現多線程和多處理方法
  • Windows 網絡編程:進程與線程
    不要企圖拿來做壞事,因為我們的演示代碼很輕易地會被殺毒軟體殺掉,我們的目的是學習編程知識。我們要完成一個模擬的「下載者」,既然是「下載者」,重要的當然是文件的下載了。文件的下載方式比較多,但是相對簡單而又比較常用的函數是URLDownloadToFile(),這個函數也是經常出現在「下載者」中的函數,該函數的定義如下:
  • 淺析多線程數據訪問一致性問題及解決方法
    CPU採用Cache機制帶來多線程訪問共享數據的一致性或數據可見性問題,是從事多線程編程的工程師們繞不過去的坎兒。    知道了問題所在,解決並發訪問下數據一致性問題的方法有多種。最常用的就是加鎖方式,比如C/C++下直接使用線程鎖實現訪問互斥,同時也能保證數據修改後的可見性。
  • Java多線程:詳解線程間通信的四種方式!
    源 / Java知音       文 /  hapjin一,介紹本總結我對於JAVA多線程中線程之間的通信方式的理解A和線程B持有同一個MyObject類的對象object,儘管這兩個線程需要調用不同的方法,但是它們是同步執行的,比如:線程B需要等待線程A執行完了methodA()方法之後,它才能執行methodB()方法。
  • 多線程渲染
    本文將講述多線程渲染的動機,實現方法及遇到的問題二、多線程渲染背景介紹(圖一:單線程渲染流程)(圖一)展示的是單線程渲染的流程,一般情況下,在遊戲每一幀運行過程中,主線程(CPU1)先執行Update,在這裡做大量的邏輯更新,例如AI、碰撞檢測和動畫更新等,然後執行Render,在這裡做渲染相關的指令調用。
  • C++11多線程編程(二)——互斥鎖mutex用法
    還是那個問題,編程世界中學習一個新的技術點,一定要明白一件事,為什麼要出現這個技術點,只有弄懂了這個才能從根本上有學習的動力。