1.実験の目的
(1)オペレーティングシステムのスレッドとアプリケーションプログラミングプロセス
の概念を理解します。(2)スレッドの同期とプログラミングの概念を理解します。
2.実験内容
(1)fork関数を使用して、UbuntuまたはFedora環境で親プロセスと子プロセスのペアを作成し、それぞれ独自のプロセス番号とプロンプトメッセージ文字列を出力します。
(2)pthread_create関数を使用して、UbuntuまたはFedora環境で2つのスレッドAおよびBを作成します。スレッドAはwhileループを使用して、画面に1から1000までの自然数を順次出力します。スレッドBは、whileループを使用して、画面に1000から1までの自然数を順次出力します。出力が速すぎないように、0.5秒ごとに数値を出力してください。
(3)Windows環境では、高級言語プログラミング環境(VS環境またはVC環境に限定)を使用してCreateThread関数を呼び出し、(2)の機能を実現します。
(4)Windows環境では、高級言語プログラミング環境(VS環境またはVC環境に限定)を使用して、CreateThread関数および関連する同期関数を呼び出し、「生産者/消費者」問題の実現をシミュレートします。
(5)Windows環境では、高級言語プログラミング環境(VS環境またはVC環境に限定)を使用してCreateThread関数を呼び出し、「円と正方形を同時に描画する」ことを実現します。円の中心、半径、色、正方形の中心、辺の長さ、色、その他のパラメータは自分で決定します。円と正方形の境界で720ポイントを取ることをお勧めします。描画プロセスを視覚的に示すために、各ポイントが描画された後、0.2秒から0.5秒の間スリープします。
3.実験プロセス
(1)実験ステップ
1)親子プロセスを作成するためのフォーク関数
1. ubuntuでテキストエディターを開き、cプログラム(problem1.c)を
記述します。2。コアコード
1. void main()
2. {
pid_t pid;
3. fprintf(stdout,"I am the first process, my pid is %d, my parent pid is %d\n",getpid(),getppid());
4. pid=fork();
5. if(0==pid)
6. fprintf(stdout,"I am the child process, my pid value is %d, but my real pid is %d, my parent pid is %d\n",pid,getpid(),getppid());
7. else
8. fprintf(stdout,"I am the parent process, my child pid value is %d, my real pid is %d, my parent pid is %d\n",pid,getpid(),getppid());
9. }
3. 3行目では、現在のプロセスのpidとその親プロセスのpidを出力する必要があります。4行目のpidは、fork()関数の戻り値です。6行目と7行目は、それぞれ実行時にさまざまなプロセスに関する情報を出力します。 (getpid()、getppid()は、それぞれプロセスとその親プロセスのpidを返します)
4。ターミナルでプログラムをコンパイルし、結果を出力します
#gcc problem1.c
#。/ a.out5
。分析結果
2)pthread_create関数は2つのスレッドを作成します(problem2.c)1。ubuntuで
テキストエディターを開き、cプログラムを記述します
2.コアコード
10. int main()
11. {
12. pthread_t tips1;
13. pthread_t tips2;
14. int hThread1 = pthread_create(&tips1, NULL, print1, NULL);
15. int hThread2 = pthread_create(&tips2, NULL, print2, NULL);
16. if (hThread1 != 0)
17. {
18. printf("hThread1 err");
19. }
20. else if (hThread2 != 0)
21. {
22. printf("hThread2 err!");
23. }
24. void *result1;
25. void *result2;
26. pthread_join(tips1, &result1);
27. pthread_join(tips2, &result2);
28. return 0;
29. }
- pthread_create(,,,)はスレッドを作成し、最初のパラメーターはスレッドIDへのポインター、2番目のパラメーターはスレッド属性の設定に使用され、3番目のパラメーターはスレッド実行関数の開始アドレス、最後のパラメーターは実行中の関数パラメーター。戻り値:成功した場合は0を返し、それ以外の場合はエラー番号を返します
。pthread_join(、)は、スレッドの終了を待機するために使用されます。最初のパラメーターは待機中のスレッドのIDであり、2番目のパラメーターは待機中のスレッドの戻り値を格納するために使用できるユーザー定義のポインターです。この関数はスレッドブロック関数です。この関数を呼び出す関数は、待機中のスレッドが終了するまで待機します。関数が戻ると、待機中のスレッドのリソースが回復されます。実行が成功した場合は0を返し、失敗した場合はエラー番号を返します。
4.端末でプログラムをコンパイルし、結果
#gcc problem1.c -lpthread
#。/ a.outを出力し
ます。5。解析結果
3)VS環境が(2)の機能を実現
1. VS write cプログラム(problem3)を開きます。 cpp)
2。コアコード
30. int main()
31. {
32. HANDLE hThread[2];
33. //创建线程,并调用函数打印输出
34. hThread[0] = CreateThread(NULL, 0, print1, (LPVOID)1000, 0, NULL);
35. hThread[1] = CreateThread(NULL, 0, print2, (LPVOID)1, 0, NULL);
36. //等待所有线程结束
37. WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
38.
39. CloseHandle(hThread[0]);
40. CloseHandle(hThread[1]);
41.
42. return 0;
43. }
- C / C ++では、CreateThread関数を使用してプロセスでスレッドを作成できます。関数の具体的な形式は次のとおりです。
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadID
);
パラメータの意味は次のとおりです
。lpThreadAttrivutes:新しいスレッドのセキュリティ属性を定義するために使用されるSECURITY_ATTRIBUTESへのポインタ。通常はNULLに設定されます
。dwStackSize:バイト単位で割り当てられたスレッドスタックのサイズ。デフォルト値は0です
。lpStartAddress :スレッド関数アドレスを指します。各スレッドには、スレッドの特定の実行コードである独自のスレッド関数があります
。lpParameter:スレッド関数に渡されるパラメーター
。dwCreationFlags:作成されたスレッドの実行ステータスを示します。CREATE_SUSPENDは現在作成されているスレッドを一時停止することを意味し、0は、現在作成されているプロセスをすぐに実行することを意味します
。lpThreadID:新しく作成されたスレッドのID番号を返します。
関数呼び出しが成功した場合は、新しいスレッドのハンドルを返し、WaitForSingleObject関数を呼び出して、作成されたスレッドの終了を待ちます。関数の形式は次のとおり
です
。DWORDWaitForSingleObject (HANDLE hHandle、
DWORD dwMilliseconds
);
パラメーターの意味は次のとおりです
。hHandle:指定されたオブジェクトまたは時間のハンドル;
dwMilliseconds:待機時間(ミリ秒単位)。待機時間が待機時間を超えると、この関数は戻ります。パラメータが0に設定されている場合、関数はすぐに戻ります。INFINITEに設定されている場合、関数はシグナルが発生するまで戻りません。4.プログラムをデバッグして実行し
ます。5。結果を分析します。
4)「生産者/消費者」問題を達成するためのVS環境シミュレーション
1. VSを開いてcプログラム(problem4.cpp)を記述し
ます。2。スレッドの並行生産者/消費者モデル:1。2つのプロセスが同じメモリリソースで動作します。1つは生産者と他は消費者です。2.プロデューサーは共有メモリリソースにデータを入力し、領域がいっぱいになると、コンシューマーがデータを消費するのを待ちます。3.コンシューマーは、共有メモリリソースからデータをフェッチし、領域が空の場合、プロデューサーがデータを入力するのを待ちます。4.プロデューサーのデータ入力動作とコンシューマーのデータ消費動作を同時に発生させることはできません。、
3。コアコード
44. DWORD WINAPI producer(LPVOID v)
45. {
46. int item;
47. while (true)
48. {
49. item = produce_item();
50. empty--;
51. if (empty < 0)
52. empty++;
53. else if (mutex > 0)
54. {
55. mutex--;
56. insert_item(item);
57. full++;
58. mutex++;
59. }
60. Sleep(2000);
61. }
62. return 1;
63. }
64.
65. DWORD WINAPI consumer(LPVOID v)
66. {
67. int item;
68. while (true)
69. {
70. full--;
71. if (full < 0)
72. full++;
73. else if (mutex > 0)
74. {
75. mutex--;
76. item = remove_item();
77. empty++;
78. mutex++;
79. }
80. Sleep(2000);
81. }
82. return 1;
83. }
- DWORD WINAPIプロデューサー(LPVOID v)、DWORD WINAPIコンシューマー(LPVOID v)は、プロデューサーとコンシューマーの2つのスレッドがPV操作を通じて効果的かつ同時に実行できることを意味します。2つのスレッドはバッファー内で相互に排他的であり、セマフォmutex = 1(初期値);バッファストレージを表すにはfullとemptyを使用します; insert_item(item)、remove_item()はバッファへのデータの保存と削除を表します。
4.プログラムをデバッグして実行し
ます。5。結果を分析します
。5)
円と正方形を同時に描画するVS環境1. VSを開いてcプログラム(problem5.cpp)を作成し、最初にEasyXを使用してキャンバスを作成します
。2。コアコード
DWORD WINAPI circle(LPVOID n)
85. {
86. double r = 200.0,x0=250.0,y0=250.0;//半径与圆心
87. int x1,x2,y;
88. for (y=50;y<=450;y+=6)
89. {
90. double yy = sqrt(r*r-(y-y0)*(y-y0));
91. x1 = x0 - yy;
92. x2 = x0 + yy;
93. outtextxy(x1,y,c);
94. Sleep(100);
95. outtextxy(x2,y,c);
96. Sleep(100);
97. }
98. return 0;
99. }
100.
101. //画方
102. DWORD WINAPI tangle(LPVOID n)
103. {
104. int mx = 540, my = 50;//左上角
105. for (; my < 300; my+=10)
106. {
107. outtextxy(mx,my,c);
108. Sleep(100);
109. }
110. for (; mx < 940; mx += 10)
111. {
112. outtextxy(mx, my, c);
113. Sleep(100);
114. }
115. for (; my > 50; my -= 10)
116. {
117. outtextxy(mx, my, c);
118. Sleep(100);
119. }
120. for (; mx > 540; mx -= 10)
121. {
122. outtextxy(mx, my, c);
123. Sleep(100);
124. }
125. return 0;}
3.円と長方形の辺をキャンバス上の点の形で1つずつ出力します。EasyXでouttestxy()関数を使用すると、円と長方形上の点の座標を使用する方が便利です。
4.プログラムをデバッグして実行し
ます。5。結果を分析します。
(2)エラーの解決と最適化
1.操作プロセスが間違っており、端末がC言語プログラムをコンパイルするときに対応するファイルが見つかりません。cプログラムをデスクトップに保存したので、端末のデフォルトの場所はホームディレクトリです。それが見つからないので、ファイルをホームディレクトリに移動するだけです。
2.特殊な構文エラー、プログラム実行結果の出力時のエラー。コマンドが正しく入力されていません。#。/ a.outである必要があります。「。」は無視しないでください。
3.コンパイルエラー、problem2.cはコンパイルできません。静的リンクライブラリを呼び出すためにコンパイルするときは、-lpthreadパラメータを追加することに注意してください。pthreadはLinuxシステムのデフォルトライブラリではないためです。
4.特別な文法エラー、
CloseHandleの呼び出しが成功すると、関連するカーネルオブジェクトの参照カウントが1つ減ります。この関数は非常に多くの作業を行います。カーネルオブジェクトを実際に閉じるわけではなく、カウントを1つ減らすだけで、CreateThread()は正しい値を返します。
5.操作エラー、プロデューサーとコンシューマーのアクセス数が正しくありません。2つのPV操作が適切な順序で配置されていないため、エラーが発生します。絵を描いて、PV操作の実行シーケンスを分析します。
6.操作エラー、円と長方形の形状が間違っています。当初、EasyXは絵を描くために使用されておらず、座標を決定するためにスペースが使用されていたため、円と長方形のスペースが混乱し、不正確な形状になりました。
EasyXでキャンバスを作成した後、エラーを回避するためにキャンバス上の座標を直接使用できます。
7.操作エラー、長方形の辺の長さの出力が空です。その理由は、座標間の間隔が狭すぎて文字が重なっているため、すべてを表示できず、文字間の距離を大きくするだけです。
第4に、結果
1)fork関数は親と子を作成します
fork()関数コンテキストは異なるプロセスで異なる値を返し、pidを返します子プロセスは親プロセスであり、エラーが負の数の場合は子で0を返します;
出力最初の文は現在のプロセスとその親プロセスのpidであり、現在のプロセスpidは18239であり、次に子プロセスが作成されます。pidは親プロセスでは18240、子プロセスでは0です。2番目の文の出力は親プロセスの出力。3つの文は子プロセスから出力されます。
2)pthread_create関数は2つのスレッドを作成します
。図からわかるように、2つのスレッドは同時に正常に実行されます。
3)VS環境は(2)の機能を実現します
この図から、2つのスレッドが同時に正常に実行されていることがわかります。
4)VS環境シミュレーションは「生産者/消費者」問題を実現します
。写真からわかるように、生産者と消費者の両方の生産と消費が互いに遅滞なく同時に実行され、アクセス数がバッファに取り出された数値は正しいです。
5)VS環境では、円と正方形を同時に描画し
ます。図から、円と正方形の2スレッド同時描画が正常に行われていることがわかります。
V.経験
この実験を通して、親子プロセスとマルチスレッドについて予備的に理解しました。PV操作の意味を理解し、その使用法を習得できます。将来のプログラミングでいくつかの問題にうまく対処できますが、現在のレベルは習熟度はまだ十分ではありません、まだもっと練習が必要です。