(1): linux command
Mount USB:
mount -t vfat /dev/sda1 /mnt/cdrom
Unmount USB:
umount /mnt/cdrom
copy directory recyle:
cp -r apache-ant-1.7.0 /usr/local
unpackage file:
tar -zxvf apache-ant-1.7.0-bin.tar.gz
ctr+alt+f4 swich to dos interface
alt+f7 return to graphic
find / -name filename
ping local ip : ifconfig
vi usage :
[esc] [insert]
:w write
:q quit
(2): software install
From the Ant FAQ:
How do you get ant-1.6.x (or any version later than 1.5.2) to work on on RedHat ES 3?
Redhat ES 3.0 comes installed with ant 1.5.2. Even if you have your PATH and ANT_HOME variables set correctly to a later version of ant, you will always be forced to use the preinstalled version.
To use a later version of ant on this OS you could do the following:
$ ant -version
Apache Ant version 1.5.2-23 compiled on November 12 2003
$ su -l
# rpm -e ant ant-libs
# exit
$ hash -r
$ ant -version
Apache Ant version 1.6.2 compiled on July 16 2004
But on my computer similar do like this:
[root@localhost TestAntBuld]# ant -version
Apache Ant version 1.5.2-20 compiled on September 25 2003
[root@localhost root]# sudo -l
User root may run the following commands on this host:
(ALL) ALL
[root@localhost root]# rpm -e ant ant-libs
[root@localhost root]# exit
logout
[root@localhost root]# bash -r
[root@localhost root]# exit
exit
[root@localhost root]# ant -version
Apache Ant version 1.7.0 compiled on December 13 2006
Now, That is ok!
Install Junit
1 unzip the download zip file
2 set path (/etc/profile and /etc/profile.d/java.sh)
Install PostgreSQL
1 gunzip postgresql-8.2.3.tar.gz
tar xf postgresql-8.2.3.tar
This will create a directory postgresql-8.2.3 under the current directory with the PostgreSQL sources. Change into that directory for the rest of the installation procedure.
2 ./configure
3 gmake
4 gmake check
5 gmake install
6 To undo the installation use the command gmake uninstall. However, this will not remove any created directories.
LD_LIBRARY_PATH=/usr/local/pgsql/lib
export LD_LIBRARY_PATH
8 set enviroment path
PATH=/usr/local/pgsql/bin:$PATH
export PATH
JUnit Test:
*** You must first compile the java classes,afer that, then you can test it!
run the test:
*
To run the test from the console, type:
java org.junit.runner.JUnitCore junitfaq.SimpleTest
*
To run the test with the test runner used in main(), type:
java junitfaq.SimpleTest
The passing test results in the following textual output:
.
Time: 0
OK (1 tests)
(3): intro_postgreSQL_startup
Before you can do anything, you must initialize a database storage area on disk. We call this a database cluster.
The desired file system location of your database cluster is indicated by the -D option, for example:
$ initdb -D /usr/local/pgsql/data
anyone can access the database, you must start the database server. The
database server program is called postgres. The postgres program
must know where to find the data it is supposed to use. This is done
with the -D option. Thus, the simplest way to start the server is:
$ postgres -D /usr/local/pgsql/data
which will leave the server running in the foreground. This must be
done while logged into the PostgreSQL user account. Without -D, the
server will try to use the data directory named by the environment
variable PGDATA. If that variable is not provided either, it will fail.
Normally it is better to start postgres in the background. For this, use the usual shell syntax:
$ postgres -D /usr/local/pgsql/data >logfile 2>&1 &
ping -n 5 -l 1472 -f www.sina.com.cn
LDAP:
如果你把 ldap 安裝為 windows 服務(wù),你可以像我一樣啟動(dòng):
net start OpenLDAP-slapd
net stop OpenLDAP-slapd
ldapadd -x -D "cn=Manager,dc=grid,dc=nwu" -W -f example.ldif
** 注意: 在第一次添加的時(shí)候要講默認(rèn)的庫(kù) 添加進(jìn)去才可以,否則會(huì)報(bào)錯(cuò)誤 Object 找不到!?。?br>dc=example,dc=com is not there by default, you have to add it as the first entry.
Your when you are trying to add you are assuming dc=example,dc=com is there,
just remove the fred walter and try again
將每一個(gè)目錄ENTRY看作是一個(gè)統(tǒng)一表的一行,然后把屬性看作列
FAQ:
1:
windows下的openldap,filter 不能家單引號(hào) 如下(去掉單引號(hào))
ldapsearch -x -b dc=grid,dc=nwu (objectclass=*)
2:
用到的objectclass (處在schema中定義) 必須在 slapd.conf 中include進(jìn)來(lái),否則報(bào)錯(cuò)誤
3:
dn: cn=JSmith,ou=Managerment,dc=grid,dc=nwu
cn: James Smith,Jim Smith,Jimmy Smith
這里的dn中的cn 和屬性中的cn 必須是一樣的,否則報(bào)錯(cuò)誤:alue of naming attribute 'cn' is not present in entry
4:擴(kuò)展schema
http://blog.csdn.net/wcy1323/archive/2006/04/26/678459.aspx
synchronized是利用鎖的機(jī)制,使變量或代碼塊在某一時(shí)該只能被一個(gè)線程訪問(wèn)。
而ThreadLocal為每一個(gè)線程都提供了變量的副本,使得每個(gè)線程在某一時(shí)間訪問(wèn)到的并不是同一個(gè)對(duì)象,
這樣就隔離了多個(gè)線程對(duì)數(shù)據(jù)的數(shù)據(jù)共享。
而Synchronized卻正好相反,它用于在多個(gè)線程間通信時(shí)能夠獲得數(shù)據(jù)共享。
Synchronized用于線程間的數(shù)據(jù)共享,而ThreadLocal則用于線程間的數(shù)據(jù)隔離。
當(dāng)然ThreadLocal并不能替代synchronized,它們處理不同的問(wèn)題域。
Synchronized用于實(shí)現(xiàn)同步機(jī)制,比ThreadLocal更加復(fù)雜。
It turns out that every JavaScript object includes an internal reference to another object, known as its prototype object. Any properties of the prototype appear to be properties of an object for which it is the prototype. Another way of saying this is that a JavaScript object inherits properties from its prototype.
In the previous section, I showed that the new operator creates a new, empty object and then invokes a constructor function as a method of that object. This is not the complete story, however. After creating the empty object, new sets the prototype of that object. The prototype of an object is the value of the prototype property of its constructor function. All functions have a prototype property that is automatically created and initialized when the function is defined. The initial value of the prototype property is an object with a single property. This property is named constructor and refers back to the constructor function with which the prototype is associated. (You may recall the constructor property from Chapter 7 ; this is why every object has a constructor property.) Any properties you add to this prototype object will appear to be properties of objects initialized by the constructor.
對(duì)要訪問(wèn)的同步資源進(jìn)行 同步計(jì)數(shù)控制,來(lái)達(dá)到同步訪問(wèn)資源的目的。
1:互斥條件 ,即資源是不能夠被共享的。
2:至少有一個(gè)進(jìn)程在使用一個(gè)資源卻在等待另外一個(gè)線程所持有的一個(gè)資源
3:資源部能夠被進(jìn)程搶占。
4 :必須有循環(huán)的等待
那么要解除死鎖,只要讓這幾個(gè)條件中的一個(gè)不成立就可以了。例如:
哲學(xué)家問(wèn)題,如果每個(gè)哲學(xué)家都是先 取左邊的筷子,在取右邊的筷子,那么很有可能就會(huì)出現(xiàn)死鎖了,那么我們可以讓最后的一個(gè)哲學(xué)家是先取右邊的筷子,然后再取左邊的筷子,那么就破壞了循環(huán)等待的原則,那么死鎖自然也就不會(huì)成立了 。當(dāng)然線程也可以一次獲得所有的所需資源來(lái)實(shí)現(xiàn)了。
volatile
do?
This is probably best explained by comparing the effects that volatile
and synchronized
have on a method. volatile
is a field modifier, while synchronized
modifies code blocks and methods. So we can specify three variations of a simple accessor using those two keywords:
int i1; int geti1() {return i1;} volatile int i2; int geti2() {return i2;} int i3; synchronized int geti3() {return i3;}
geti1()
accesses the value currently stored in i1
in the current thread. Threads can have local copies of variables, and the data does not have to be the same as the data held in other threads. In particular, another thread may have updated i1
in it's thread, but the value in the current thread could be different from that updated value. In fact Java has the idea of a "main" memory, and this is the memory that holds the current "correct" value for variables. Threads can have their own copy of data for variables, and the thread copy can be different from the "main" memory. So in fact, it is possible for the "main" memory to have a value of 1 for i1
, for thread1 to have a value of 2 for i1
and for thread2 to have a value of 3 for i1
if thread1 and thread2 have both updated i1
but those updated value has not yet been propagated to "main" memory or other threads.
On the other hand, geti2()
effectively accesses the value of i2
from "main" memory. A volatile
variable is not allowed to have a local copy of a variable that is different from the value currently held in "main" memory. Effectively, a variable declared volatile
must have it's data synchronized across all threads, so that whenever you access or update the variable in any thread, all other threads immediately see the same value. Of course, it is likely that volatile
variables have a higher access and update overhead than "plain" variables, since the reason threads can have their own copy of data is for better efficiency.
Well if volatile
already synchronizes data across threads, what is synchronized
for? Well there are two differences. Firstly synchronized
obtains and releases locks on monitors which can force only one thread at a time to execute a code block, if both threads use the same monitor (effectively the same object lock). That's the fairly well known aspect to synchronized
. But synchronized
also synchronizes memory. In fact synchronized
synchronizes the whole of thread memory with "main" memory. So executing geti3()
does the following:
this
(assuming the monitor is unlocked, otherwise the thread waits until the monitor is unlocked).
i3
, which may have just been reset from "main" memory).
geti3()
we have no changes.)
this
. So where volatile
only synchronizes the value of one variable between thread memory and "main" memory, synchronized
synchronizes the value of all variables between thread memory and "main" memory, and locks and releases a monitor to boot. Clearly synchronized
is likely to have more overhead than volatile
.
Visual C++線程同步技術(shù)剖析:臨界區(qū),時(shí)間,信號(hào)量,互斥量 | |
|
摘要: 多線程同步技術(shù)是計(jì)算機(jī)軟件開(kāi)發(fā)的重要技術(shù),本文對(duì)多線程的各種同步技術(shù)的原理和實(shí)現(xiàn)進(jìn)行了初步探討。
關(guān)鍵詞: VC++6.0; 線程同步;臨界區(qū);事件;互斥;信號(hào)量;
正文
使線程同步
在程序中使用多線程時(shí),一般很少有多個(gè)線程能在其生命期內(nèi)進(jìn)行完全獨(dú)立的操作。更多的情況是一些線程進(jìn)行某些處理操作,而其他的線程必須對(duì)其處理結(jié)果進(jìn)行了解。正常情況下對(duì)這種處理結(jié)果的了解應(yīng)當(dāng)在其處理任務(wù)完成后進(jìn)行。
如果不采取適當(dāng)?shù)拇胧渌€程往往會(huì)在線程處理任務(wù)結(jié)束前就去訪問(wèn)處理結(jié)果,這就很有可能得到有關(guān)處理結(jié)果的錯(cuò)誤了解。例如,多個(gè)線程同時(shí)訪問(wèn)同一個(gè)全局變量,如果都是讀取操作,則不會(huì)出現(xiàn)問(wèn)題。如果一個(gè)線程負(fù)責(zé)改變此變量的值,而其他線程負(fù)責(zé)同時(shí)讀取變量?jī)?nèi)容,則不能保證讀取到的數(shù)據(jù)是經(jīng)過(guò)寫線程修改后的。
為了確保讀線程讀取到的是經(jīng)過(guò)修改的變量,就必須在向變量寫入數(shù)據(jù)時(shí)禁止其他線程對(duì)其的任何訪問(wèn),直至賦值過(guò)程結(jié)束后再解除對(duì)其他線程的訪問(wèn)限制。象這種保證線程能了解其他線程任務(wù)處理結(jié)束后的處理結(jié)果而采取的保護(hù)措施即為線程同步。
線程同步是一個(gè)非常大的話題,包括方方面面的內(nèi)容。從大的方面講,線程的同步可分用戶模式的線程同步和內(nèi)核對(duì)象的線程同步兩大類。用戶模式中線程的同步方法主要有原子訪問(wèn)和臨界區(qū)等方法。其特點(diǎn)是同步速度特別快,適合于對(duì)線程運(yùn)行速度有嚴(yán)格要求的場(chǎng)合。
內(nèi)核對(duì)象的線程同步則主要由事件、等待定時(shí)器、信號(hào)量以及信號(hào)燈等內(nèi)核對(duì)象構(gòu)成。由于這種同步機(jī)制使用了內(nèi)核對(duì)象,使用時(shí)必須將線程從用戶模式切換到內(nèi)核模式,而這種轉(zhuǎn)換一般要耗費(fèi)近千個(gè)CPU周期,因此同步速度較慢,但在適用性上卻要遠(yuǎn)優(yōu)于用戶模式的線程同步方式。
臨界區(qū)
臨界區(qū)(Critical Section)是一段獨(dú)占對(duì)某些共享資源訪問(wèn)的代碼,在任意時(shí)刻只允許一個(gè)線程對(duì)共享資源進(jìn)行訪問(wèn)。如果有多個(gè)線程試圖同時(shí)訪問(wèn)臨界區(qū),那么在有一個(gè)線程進(jìn)入后其他所有試圖訪問(wèn)此臨界區(qū)的線程將被掛起,并一直持續(xù)到進(jìn)入臨界區(qū)的線程離開(kāi)。臨界區(qū)在被釋放后,其他線程可以繼續(xù)搶占,并以此達(dá)到用原子方式操作共享資源的目的。
臨界區(qū)在使用時(shí)以CRITICAL_SECTION結(jié)構(gòu)對(duì)象保護(hù)共享資源,并分別用EnterCriticalSection()和LeaveCriticalSection()函數(shù)去標(biāo)識(shí)和釋放一個(gè)臨界區(qū)。所用到的CRITICAL_SECTION結(jié)構(gòu)對(duì)象必須經(jīng)過(guò)InitializeCriticalSection()的初始化后才能使用,而且必須確保所有線程中的任何試圖訪問(wèn)此共享資源的代碼都處在此臨界區(qū)的保護(hù)之下。否則臨界區(qū)將不會(huì)起到應(yīng)有的作用,共享資源依然有被破壞的可能。
圖1 使用臨界區(qū)保持線程同步
下面通過(guò)一段代碼展示了臨界區(qū)在保護(hù)多線程訪問(wèn)的共享資源中的作用。通過(guò)兩個(gè)線程來(lái)分別對(duì)全局變量g_cArray[10]進(jìn)行寫入操作,用臨界區(qū)結(jié)構(gòu)對(duì)象g_cs來(lái)保持線程的同步,并在開(kāi)啟線程前對(duì)其進(jìn)行初始化。為了使實(shí)驗(yàn)效果更加明顯,體現(xiàn)出臨界區(qū)的作用,在線程函數(shù)對(duì)共享資源g_cArray[10]的寫入時(shí),以Sleep()函數(shù)延遲1毫秒,使其他線程同其搶占CPU的可能性增大。如果不使用臨界區(qū)對(duì)其進(jìn)行保護(hù),則共享資源數(shù)據(jù)將被破壞(參見(jiàn)圖1(a)所示計(jì)算結(jié)果),而使用臨界區(qū)對(duì)線程保持同步后則可以得到正確的結(jié)果(參見(jiàn)圖1(b)所示計(jì)算結(jié)果)。代碼實(shí)現(xiàn)清單附下:
// 臨界區(qū)結(jié)構(gòu)對(duì)象 CRITICAL_SECTION g_cs; // 共享資源 char g_cArray[10]; UINT ThreadProc10(LPVOID pParam) { // 進(jìn)入臨界區(qū) EnterCriticalSection(&g_cs); // 對(duì)共享資源進(jìn)行寫入操作 for (int i = 0; i < 10; i++) { g_cArray[i] = 'a'; Sleep(1); } // 離開(kāi)臨界區(qū) LeaveCriticalSection(&g_cs); return 0; } UINT ThreadProc11(LPVOID pParam) { // 進(jìn)入臨界區(qū) EnterCriticalSection(&g_cs); // 對(duì)共享資源進(jìn)行寫入操作 for (int i = 0; i < 10; i++) { g_cArray[10 - i - 1] = 'b'; Sleep(1); } // 離開(kāi)臨界區(qū) LeaveCriticalSection(&g_cs); return 0; } …… void CSample08View::OnCriticalSection() { // 初始化臨界區(qū) InitializeCriticalSection(&g_cs); // 啟動(dòng)線程 AfxBeginThread(ThreadProc10, NULL); AfxBeginThread(ThreadProc11, NULL); // 等待計(jì)算完畢 Sleep(300); // 報(bào)告計(jì)算結(jié)果 CString sResult = CString(g_cArray); AfxMessageBox(sResult); } |
在使用臨界區(qū)時(shí),一般不允許其運(yùn)行時(shí)間過(guò)長(zhǎng),只要進(jìn)入臨界區(qū)的線程還沒(méi)有離開(kāi),其他所有試圖進(jìn)入此臨界區(qū)的線程都會(huì)被掛起而進(jìn)入到等待狀態(tài),并會(huì)在一定程度上影響。程序的運(yùn)行性能。尤其需要注意的是不要將等待用戶輸入或是其他一些外界干預(yù)的操作包含到臨界區(qū)。如果進(jìn)入了臨界區(qū)卻一直沒(méi)有釋放,同樣也會(huì)引起其他線程的長(zhǎng)時(shí)間等待。換句話說(shuō),在執(zhí)行了EnterCriticalSection()語(yǔ)句進(jìn)入臨界區(qū)后無(wú)論發(fā)生什么,必須確保與之匹配的LeaveCriticalSection()都能夠被執(zhí)行到??梢酝ㄟ^(guò)添加結(jié)構(gòu)化異常處理代碼來(lái)確保LeaveCriticalSection()語(yǔ)句的執(zhí)行。雖然臨界區(qū)同步速度很快,但卻只能用來(lái)同步本進(jìn)程內(nèi)的線程,而不可用來(lái)同步多個(gè)進(jìn)程中的線程。
MFC為臨界區(qū)提供有一個(gè)CCriticalSection類,使用該類進(jìn)行線程同步處理是非常簡(jiǎn)單的,只需在線程函數(shù)中用CCriticalSection類成員函數(shù)Lock()和UnLock()標(biāo)定出被保護(hù)代碼片段即可。對(duì)于上述代碼,可通過(guò)CCriticalSection類將其改寫如下:
// MFC臨界區(qū)類對(duì)象 CCriticalSection g_clsCriticalSection; // 共享資源 char g_cArray[10]; UINT ThreadProc20(LPVOID pParam) { // 進(jìn)入臨界區(qū) g_clsCriticalSection.Lock(); // 對(duì)共享資源進(jìn)行寫入操作 for (int i = 0; i < 10; i++) { g_cArray[i] = 'a'; Sleep(1); } // 離開(kāi)臨界區(qū) g_clsCriticalSection.Unlock(); return 0; } UINT ThreadProc21(LPVOID pParam) { // 進(jìn)入臨界區(qū) g_clsCriticalSection.Lock(); // 對(duì)共享資源進(jìn)行寫入操作 for (int i = 0; i < 10; i++) { g_cArray[10 - i - 1] = 'b'; Sleep(1); } // 離開(kāi)臨界區(qū) g_clsCriticalSection.Unlock(); return 0; } …… void CSample08View::OnCriticalSectionMfc() { // 啟動(dòng)線程 AfxBeginThread(ThreadProc20, NULL); AfxBeginThread(ThreadProc21, NULL); // 等待計(jì)算完畢 Sleep(300); // 報(bào)告計(jì)算結(jié)果 CString sResult = CString(g_cArray); AfxMessageBox(sResult); } |
管理事件內(nèi)核對(duì)象
在前面講述線程通信時(shí)曾使用過(guò)事件內(nèi)核對(duì)象來(lái)進(jìn)行線程間的通信,除此之外,事件內(nèi)核對(duì)象也可以通過(guò)通知操作的方式來(lái)保持線程的同步。對(duì)于前面那段使用臨界區(qū)保持線程同步的代碼可用事件對(duì)象的線程同步方法改寫如下:
// 事件句柄 HANDLE hEvent = NULL; // 共享資源 char g_cArray[10]; …… UINT ThreadProc12(LPVOID pParam) { // 等待事件置位 WaitForSingleObject(hEvent, INFINITE); // 對(duì)共享資源進(jìn)行寫入操作 for (int i = 0; i < 10; i++) { g_cArray[i] = 'a'; Sleep(1); } // 處理完成后即將事件對(duì)象置位 SetEvent(hEvent); return 0; } UINT ThreadProc13(LPVOID pParam) { // 等待事件置位 WaitForSingleObject(hEvent, INFINITE); // 對(duì)共享資源進(jìn)行寫入操作 for (int i = 0; i < 10; i++) { g_cArray[10 - i - 1] = 'b'; Sleep(1); } // 處理完成后即將事件對(duì)象置位 SetEvent(hEvent); return 0; } …… void CSample08View::OnEvent() { // 創(chuàng)建事件 hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); // 事件置位 SetEvent(hEvent); // 啟動(dòng)線程 AfxBeginThread(ThreadProc12, NULL); AfxBeginThread(ThreadProc13, NULL); // 等待計(jì)算完畢 Sleep(300); // 報(bào)告計(jì)算結(jié)果 CString sResult = CString(g_cArray); AfxMessageBox(sResult); } |
在創(chuàng)建線程前,首先創(chuàng)建一個(gè)可以自動(dòng)復(fù)位的事件內(nèi)核對(duì)象hEvent,而線程函數(shù)則通過(guò)WaitForSingleObject()等待函數(shù)無(wú)限等待hEvent的置位,只有在事件置位時(shí)WaitForSingleObject()才會(huì)返回,被保護(hù)的代碼將得以執(zhí)行。對(duì)于以自動(dòng)復(fù)位方式創(chuàng)建的事件對(duì)象,在其置位后一被WaitForSingleObject()等待到就會(huì)立即復(fù)位,也就是說(shuō)在執(zhí)行ThreadProc12()中的受保護(hù)代碼時(shí),事件對(duì)象已經(jīng)是復(fù)位狀態(tài)的,這時(shí)即使有ThreadProc13()對(duì)CPU的搶占,也會(huì)由于WaitForSingleObject()沒(méi)有hEvent的置位而不能繼續(xù)執(zhí)行,也就沒(méi)有可能破壞受保護(hù)的共享資源。在ThreadProc12()中的處理完成后可以通過(guò)SetEvent()對(duì)hEvent的置位而允許ThreadProc13()對(duì)共享資源g_cArray的處理。這里SetEvent()所起的作用可以看作是對(duì)某項(xiàng)特定任務(wù)完成的通知。
使用臨界區(qū)只能同步同一進(jìn)程中的線程,而使用事件內(nèi)核對(duì)象則可以對(duì)進(jìn)程外的線程進(jìn)行同步,其前提是得到對(duì)此事件對(duì)象的訪問(wèn)權(quán)??梢酝ㄟ^(guò)OpenEvent()函數(shù)獲取得到,其函數(shù)原型為:
HANDLE OpenEvent( DWORD dwDesiredAccess, // 訪問(wèn)標(biāo)志 BOOL bInheritHandle, // 繼承標(biāo)志 LPCTSTR lpName // 指向事件對(duì)象名的指針 ); |
如果事件對(duì)象已創(chuàng)建(在創(chuàng)建事件時(shí)需要指定事件名),函數(shù)將返回指定事件的句柄。對(duì)于那些在創(chuàng)建事件時(shí)沒(méi)有指定事件名的事件內(nèi)核對(duì)象,可以通過(guò)使用內(nèi)核對(duì)象的繼承性或是調(diào)用DuplicateHandle()函數(shù)來(lái)調(diào)用CreateEvent()以獲得對(duì)指定事件對(duì)象的訪問(wèn)權(quán)。在獲取到訪問(wèn)權(quán)后所進(jìn)行的同步操作與在同一個(gè)進(jìn)程中所進(jìn)行的線程同步操作是一樣的。
如果需要在一個(gè)線程中等待多個(gè)事件,則用WaitForMultipleObjects()來(lái)等待。WaitForMultipleObjects()與WaitForSingleObject()類似,同時(shí)監(jiān)視位于句柄數(shù)組中的所有句柄。這些被監(jiān)視對(duì)象的句柄享有平等的優(yōu)先權(quán),任何一個(gè)句柄都不可能比其他句柄具有更高的優(yōu)先權(quán)。WaitForMultipleObjects()的函數(shù)原型為:
DWORD WaitForMultipleObjects( DWORD nCount, // 等待句柄數(shù) CONST HANDLE *lpHandles, // 句柄數(shù)組首地址 BOOL fWaitAll, // 等待標(biāo)志 DWORD dwMilliseconds // 等待時(shí)間間隔 ); |
參數(shù)nCount指定了要等待的內(nèi)核對(duì)象的數(shù)目,存放這些內(nèi)核對(duì)象的數(shù)組由lpHandles來(lái)指向。fWaitAll對(duì)指定的這nCount個(gè)內(nèi)核對(duì)象的兩種等待方式進(jìn)行了指定,為TRUE時(shí)當(dāng)所有對(duì)象都被通知時(shí)函數(shù)才會(huì)返回,為FALSE則只要其中任何一個(gè)得到通知就可以返回。dwMilliseconds在飫锏淖饔糜朐赪aitForSingleObject()中的作用是完全一致的。如果等待超時(shí),函數(shù)將返回WAIT_TIMEOUT。如果返回WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1中的某個(gè)值,則說(shuō)明所有指定對(duì)象的狀態(tài)均為已通知狀態(tài)(當(dāng)fWaitAll為TRUE時(shí))或是用以減去WAIT_OBJECT_0而得到發(fā)生通知的對(duì)象的索引(當(dāng)fWaitAll為FALSE時(shí))。如果返回值在WAIT_ABANDONED_0與WAIT_ABANDONED_0+nCount-1之間,則表示所有指定對(duì)象的狀態(tài)均為已通知,且其中至少有一個(gè)對(duì)象是被丟棄的互斥對(duì)象(當(dāng)fWaitAll為TRUE時(shí)),或是用以減去WAIT_OBJECT_0表示一個(gè)等待正常結(jié)束的互斥對(duì)象的索引(當(dāng)fWaitAll為FALSE時(shí))。 下面給出的代碼主要展示了對(duì)WaitForMultipleObjects()函數(shù)的使用。通過(guò)對(duì)兩個(gè)事件內(nèi)核對(duì)象的等待來(lái)控制線程任務(wù)的執(zhí)行與中途退出:
// 存放事件句柄的數(shù)組 HANDLE hEvents[2]; UINT ThreadProc14(LPVOID pParam) { // 等待開(kāi)啟事件 DWORD dwRet1 = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE); // 如果開(kāi)啟事件到達(dá)則線程開(kāi)始執(zhí)行任務(wù) if (dwRet1 == WAIT_OBJECT_0) { AfxMessageBox("線程開(kāi)始工作!"); while (true) { for (int i = 0; i < 10000; i++); // 在任務(wù)處理過(guò)程中等待結(jié)束事件 DWORD dwRet2 = WaitForMultipleObjects(2, hEvents, FALSE, 0); // 如果結(jié)束事件置位則立即終止任務(wù)的執(zhí)行 if (dwRet2 == WAIT_OBJECT_0 + 1) break; } } AfxMessageBox("線程退出!"); return 0; } …… void CSample08View::OnStartEvent() { // 創(chuàng)建線程 for (int i = 0; i < 2; i++) hEvents[i] = CreateEvent(NULL, FALSE, FALSE, NULL); // 開(kāi)啟線程 AfxBeginThread(ThreadProc14, NULL); // 設(shè)置事件0(開(kāi)啟事件) SetEvent(hEvents[0]); } void CSample08View::OnEndevent() { // 設(shè)置事件1(結(jié)束事件) SetEvent(hEvents[1]); } |
MFC為事件相關(guān)處理也提供了一個(gè)CEvent類,共包含有除構(gòu)造函數(shù)外的4個(gè)成員函數(shù)PulseEvent()、ResetEvent()、SetEvent()和UnLock()。在功能上分別相當(dāng)與Win32 API的PulseEvent()、ResetEvent()、SetEvent()和CloseHandle()等函數(shù)。而構(gòu)造函數(shù)則履行了原CreateEvent()函數(shù)創(chuàng)建事件對(duì)象的職責(zé),其函數(shù)原型為:
CEvent(BOOL bInitiallyOwn = FALSE, BOOL bManualReset = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL ); |
按照此缺省設(shè)置將創(chuàng)建一個(gè)自動(dòng)復(fù)位、初始狀態(tài)為復(fù)位狀態(tài)的沒(méi)有名字的事件對(duì)象。封裝后的CEvent類使用起來(lái)更加方便,圖2即展示了CEvent類對(duì)A、B兩線程的同步過(guò)程:
圖2 CEvent類對(duì)線程的同步過(guò)程示意
B線程在執(zhí)行到CEvent類成員函數(shù)Lock()時(shí)將會(huì)發(fā)生阻塞,而A線程此時(shí)則可以在沒(méi)有B線程干擾的情況下對(duì)共享資源進(jìn)行處理,并在處理完成后通過(guò)成員函數(shù)SetEvent()向B發(fā)出事件,使其被釋放,得以對(duì)A先前已處理完畢的共享資源進(jìn)行操作??梢?jiàn),使用CEvent類對(duì)線程的同步方法與通過(guò)API函數(shù)進(jìn)行線程同步的處理方法是基本一致的。前面的API處理代碼可用CEvent類將其改寫為:
// MFC事件類對(duì)象
|
信號(hào)量?jī)?nèi)核對(duì)象
信號(hào)量(Semaphore)內(nèi)核對(duì)象對(duì)線程的同步方式與前面幾種方法不同,它允許多個(gè)線程在同一時(shí)刻訪問(wèn)同一資源,但是需要限制在同一時(shí)刻訪問(wèn)此資源的最大線程數(shù)目。在用CreateSemaphore()創(chuàng)建信號(hào)量時(shí)即要同時(shí)指出允許的最大資源計(jì)數(shù)和當(dāng)前可用資源計(jì)數(shù)。一般是將當(dāng)前可用資源計(jì)數(shù)設(shè)置為最大資源計(jì)數(shù),每增加一個(gè)線程對(duì)共享資源的訪問(wèn),當(dāng)前可用資源計(jì)數(shù)就會(huì)減1,只要當(dāng)前可用資源計(jì)數(shù)是大于0的,就可以發(fā)出信號(hào)量信號(hào)。但是當(dāng)前可用計(jì)數(shù)減小到0時(shí)則說(shuō)明當(dāng)前占用資源的線程數(shù)已經(jīng)達(dá)到了所允許的最大數(shù)目,不能在允許其他線程的進(jìn)入,此時(shí)的信號(hào)量信號(hào)將無(wú)法發(fā)出。線程在處理完共享資源后,應(yīng)在離開(kāi)的同時(shí)通過(guò)ReleaseSemaphore()函數(shù)將當(dāng)前可用資源計(jì)數(shù)加1。在任何時(shí)候當(dāng)前可用資源計(jì)數(shù)決不可能大于最大資源計(jì)數(shù)。
圖3 使用信號(hào)量對(duì)象控制資源
下面結(jié)合圖例3來(lái)演示信號(hào)量對(duì)象對(duì)資源的控制。在圖3中,以箭頭和白色箭頭表示共享資源所允許的最大資源計(jì)數(shù)和當(dāng)前可用資源計(jì)數(shù)。初始如圖(a)所示,最大資源計(jì)數(shù)和當(dāng)前可用資源計(jì)數(shù)均為4,此后每增加一個(gè)對(duì)資源進(jìn)行訪問(wèn)的線程(用黑色箭頭表示)當(dāng)前資源計(jì)數(shù)就會(huì)相應(yīng)減1,圖(b)即表示的在3個(gè)線程對(duì)共享資源進(jìn)行訪問(wèn)時(shí)的狀態(tài)。當(dāng)進(jìn)入線程數(shù)達(dá)到4個(gè)時(shí),將如圖(c)所示,此時(shí)已達(dá)到最大資源計(jì)數(shù),而當(dāng)前可用資源計(jì)數(shù)也已減到0,其他線程無(wú)法對(duì)共享資源進(jìn)行訪問(wèn)。在當(dāng)前占有資源的線程處理完畢而退出后,將會(huì)釋放出空間,圖(d)已有兩個(gè)線程退出對(duì)資源的占有,當(dāng)前可用計(jì)數(shù)為2,可以再允許2個(gè)線程進(jìn)入到對(duì)資源的處理??梢钥闯?,信號(hào)量是通過(guò)計(jì)數(shù)來(lái)對(duì)線程訪問(wèn)資源進(jìn)行控制的,而實(shí)際上信號(hào)量確實(shí)也被稱作Dijkstra計(jì)數(shù)器。
使用信號(hào)量?jī)?nèi)核對(duì)象進(jìn)行線程同步主要會(huì)用到CreateSemaphore()、OpenSemaphore()、ReleaseSemaphore()、WaitForSingleObject()和WaitForMultipleObjects()等函數(shù)。其中,CreateSemaphore()用來(lái)創(chuàng)建一個(gè)信號(hào)量?jī)?nèi)核對(duì)象,其函數(shù)原型為:
HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // 安全屬性指針 LONG lInitialCount, // 初始計(jì)數(shù) LONG lMaximumCount, // 最大計(jì)數(shù) LPCTSTR lpName // 對(duì)象名指針 ); |
參數(shù)lMaximumCount是一個(gè)有符號(hào)32位值,定義了允許的最大資源計(jì)數(shù),最大取值不能超過(guò)4294967295。lpName參數(shù)可以為創(chuàng)建的信號(hào)量定義一個(gè)名字,由于其創(chuàng)建的是一個(gè)內(nèi)核對(duì)象,因此在其他進(jìn)程中可以通過(guò)該名字而得到此信號(hào)量。OpenSemaphore()函數(shù)即可用來(lái)根據(jù)信號(hào)量名打開(kāi)在其他進(jìn)程中創(chuàng)建的信號(hào)量,函數(shù)原型如下:
HANDLE OpenSemaphore( DWORD dwDesiredAccess, // 訪問(wèn)標(biāo)志 BOOL bInheritHandle, // 繼承標(biāo)志 LPCTSTR lpName // 信號(hào)量名 ); |
在線程離開(kāi)對(duì)共享資源的處理時(shí),必須通過(guò)ReleaseSemaphore()來(lái)增加當(dāng)前可用資源計(jì)數(shù)。否則將會(huì)出現(xiàn)當(dāng)前正在處理共享資源的實(shí)際線程數(shù)并沒(méi)有達(dá)到要限制的數(shù)值,而其他線程卻因?yàn)楫?dāng)前可用資源計(jì)數(shù)為0而仍無(wú)法進(jìn)入的情況。ReleaseSemaphore()的函數(shù)原型為:
BOOL ReleaseSemaphore( HANDLE hSemaphore, // 信號(hào)量句柄 LONG lReleaseCount, // 計(jì)數(shù)遞增數(shù)量 LPLONG lpPreviousCount // 先前計(jì)數(shù) ); |
該函數(shù)將lReleaseCount中的值添加給信號(hào)量的當(dāng)前資源計(jì)數(shù),一般將lReleaseCount設(shè)置為1,如果需要也可以設(shè)置其他的值。WaitForSingleObject()和WaitForMultipleObjects()主要用在試圖進(jìn)入共享資源的線程函數(shù)入口處,主要用來(lái)判斷信號(hào)量的當(dāng)前可用資源計(jì)數(shù)是否允許本線程的進(jìn)入。只有在當(dāng)前可用資源計(jì)數(shù)值大于0時(shí),被監(jiān)視的信號(hào)量?jī)?nèi)核對(duì)象才會(huì)得到通知。
信號(hào)量的使用特點(diǎn)使其更適用于對(duì)Socket(套接字)程序中線程的同步。例如,網(wǎng)絡(luò)上的HTTP服務(wù)器要對(duì)同一時(shí)間內(nèi)訪問(wèn)同一頁(yè)面的用戶數(shù)加以限制,這時(shí)可以為沒(méi)一個(gè)用戶對(duì)服務(wù)器的頁(yè)面請(qǐng)求設(shè)置一個(gè)線程,而頁(yè)面則是待保護(hù)的共享資源,通過(guò)使用信號(hào)量對(duì)線程的同步作用可以確保在任一時(shí)刻無(wú)論有多少用戶對(duì)某一頁(yè)面進(jìn)行訪問(wèn),只有不大于設(shè)定的最大用戶數(shù)目的線程能夠進(jìn)行訪問(wèn),而其他的訪問(wèn)企圖則被掛起,只有在有用戶退出對(duì)此頁(yè)面的訪問(wèn)后才有可能進(jìn)入。下面給出的示例代碼即展示了類似的處理過(guò)程:
// 信號(hào)量對(duì)象句柄
HANDLE hSemaphore;
UINT ThreadProc15(LPVOID pParam)
{
// 試圖進(jìn)入信號(hào)量關(guān)口
WaitForSingleObject(hSemaphore, INFINITE);
// 線程任務(wù)處理
AfxMessageBox("線程一正在執(zhí)行!");
// 釋放信號(hào)量計(jì)數(shù)
ReleaseSemaphore(hSemaphore, 1, NULL);
return 0;
}
UINT ThreadProc16(LPVOID pParam)
{
// 試圖進(jìn)入信號(hào)量關(guān)口
WaitForSingleObject(hSemaphore, INFINITE);
// 線程任務(wù)處理
AfxMessageBox("線程二正在執(zhí)行!");
// 釋放信號(hào)量計(jì)數(shù)
ReleaseSemaphore(hSemaphore, 1, NULL);
return 0;
}
UINT ThreadProc17(LPVOID pParam)
{
// 試圖進(jìn)入信號(hào)量關(guān)口
WaitForSingleObject(hSemaphore, INFINITE);
// 線程任務(wù)處理
AfxMessageBox("線程三正在執(zhí)行!");
// 釋放信號(hào)量計(jì)數(shù)
ReleaseSemaphore(hSemaphore, 1, NULL);
return 0;
}
……
void CSample08View::OnSemaphore()
{
// 創(chuàng)建信號(hào)量對(duì)象
hSemaphore = CreateSemaphore(NULL, 2, 2, NULL);
// 開(kāi)啟線程
AfxBeginThread(ThreadProc15, NULL);
AfxBeginThread(ThreadProc16, NULL);
AfxBeginThread(ThreadProc17, NULL);
}
圖4 開(kāi)始進(jìn)入的兩個(gè)線程
圖5 線程二退出后線程三才得以進(jìn)入
上述代碼在開(kāi)啟線程前首先創(chuàng)建了一個(gè)初始計(jì)數(shù)和最大資源計(jì)數(shù)均為2的信號(hào)量對(duì)象hSemaphore。即在同一時(shí)刻只允許2個(gè)線程進(jìn)入由hSemaphore保護(hù)的共享資源。隨后開(kāi)啟的三個(gè)線程均試圖訪問(wèn)此共享資源,在前兩個(gè)線程試圖訪問(wèn)共享資源時(shí),由于hSemaphore的當(dāng)前可用資源計(jì)數(shù)分別為2和1,此時(shí)的hSemaphore是可以得到通知的,也就是說(shuō)位于線程入口處的WaitForSingleObject()將立即返回,而在前兩個(gè)線程進(jìn)入到保護(hù)區(qū)域后,hSemaphore的當(dāng)前資源計(jì)數(shù)減少到0,hSemaphore將不再得到通知,WaitForSingleObject()將線程掛起。直到此前進(jìn)入到保護(hù)區(qū)的線程退出后才能得以進(jìn)入。圖4和圖5為上述代脈的運(yùn)行結(jié)果。從實(shí)驗(yàn)結(jié)果可以看出,信號(hào)量始終保持了同一時(shí)刻不超過(guò)2個(gè)線程的進(jìn)入。
在MFC中,通過(guò)CSemaphore類對(duì)信號(hào)量作了表述。該類只具有一個(gè)構(gòu)造函數(shù),可以構(gòu)造一個(gè)信號(hào)量對(duì)象,并對(duì)初始資源計(jì)數(shù)、最大資源計(jì)數(shù)、對(duì)象名和安全屬性等進(jìn)行初始化,其原型如下:
CSemaphore( LONG lInitialCount = 1, LONG lMaxCount = 1, LPCTSTR pstrName = NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL ); |
在構(gòu)造了CSemaphore類對(duì)象后,任何一個(gè)訪問(wèn)受保護(hù)共享資源的線程都必須通過(guò)CSemaphore從父類CSyncObject類繼承得到的Lock()和UnLock()成員函數(shù)來(lái)訪問(wèn)或釋放CSemaphore對(duì)象。與前面介紹的幾種通過(guò)MFC類保持線程同步的方法類似,通過(guò)CSemaphore類也可以將前面的線程同步代碼進(jìn)行改寫,這兩種使用信號(hào)量的線程同步方法無(wú)論是在實(shí)現(xiàn)原理上還是從實(shí)現(xiàn)結(jié)果上都是完全一致的。下面給出經(jīng)MFC改寫后的信號(hào)量線程同步代碼:
// MFC信號(hào)量類對(duì)象 CSemaphore g_clsSemaphore(2, 2); UINT ThreadProc24(LPVOID pParam) { // 試圖進(jìn)入信號(hào)量關(guān)口 g_clsSemaphore.Lock(); // 線程任務(wù)處理 AfxMessageBox("線程一正在執(zhí)行!"); // 釋放信號(hào)量計(jì)數(shù) g_clsSemaphore.Unlock(); return 0; } UINT ThreadProc25(LPVOID pParam) { // 試圖進(jìn)入信號(hào)量關(guān)口 g_clsSemaphore.Lock(); // 線程任務(wù)處理 AfxMessageBox("線程二正在執(zhí)行!"); // 釋放信號(hào)量計(jì)數(shù) g_clsSemaphore.Unlock(); return 0; } UINT ThreadProc26(LPVOID pParam) { // 試圖進(jìn)入信號(hào)量關(guān)口 g_clsSemaphore.Lock(); // 線程任務(wù)處理 AfxMessageBox("線程三正在執(zhí)行!"); // 釋放信號(hào)量計(jì)數(shù) g_clsSemaphore.Unlock(); return 0; } …… void CSample08View::OnSemaphoreMfc() { // 開(kāi)啟線程 AfxBeginThread(ThreadProc24, NULL); AfxBeginThread(ThreadProc25, NULL); AfxBeginThread(ThreadProc26, NULL); } |
互斥內(nèi)核對(duì)象
互斥(Mutex)是一種用途非常廣泛的內(nèi)核對(duì)象。能夠保證多個(gè)線程對(duì)同一共享資源的互斥訪問(wèn)。同臨界區(qū)有些類似,只有擁有互斥對(duì)象的線程才具有訪問(wèn)資源的權(quán)限,由于互斥對(duì)象只有一個(gè),因此就決定了任何情況下此共享資源都不會(huì)同時(shí)被多個(gè)線程所訪問(wèn)。當(dāng)前占據(jù)資源的線程在任務(wù)處理完后應(yīng)將擁有的互斥對(duì)象交出,以便其他線程在獲得后得以訪問(wèn)資源。與其他幾種內(nèi)核對(duì)象不同,互斥對(duì)象在操作系統(tǒng)中擁有特殊代碼,并由操作系統(tǒng)來(lái)管理,操作系統(tǒng)甚至還允許其進(jìn)行一些其他內(nèi)核對(duì)象所不能進(jìn)行的非常規(guī)操作。為便于理解,可參照?qǐng)D6給出的互斥內(nèi)核對(duì)象的工作模型:
圖6 使用互斥內(nèi)核對(duì)象對(duì)共享資源的保護(hù)
圖(a)中的箭頭為要訪問(wèn)資源(矩形框)的線程,但只有第二個(gè)線程擁有互斥對(duì)象(黑點(diǎn))并得以進(jìn)入到共享資源,而其他線程則會(huì)被排斥在外(如圖(b)所示)。當(dāng)此線程處理完共享資源并準(zhǔn)備離開(kāi)此區(qū)域時(shí)將把其所擁有的互斥對(duì)象交出(如圖(c)所示),其他任何一個(gè)試圖訪問(wèn)此資源的線程都有機(jī)會(huì)得到此互斥對(duì)象。
以互斥內(nèi)核對(duì)象來(lái)保持線程同步可能用到的函數(shù)主要有CreateMutex()、OpenMutex()、ReleaseMutex()、WaitForSingleObject()和WaitForMultipleObjects()等。在使用互斥對(duì)象前,首先要通過(guò)CreateMutex()或OpenMutex()創(chuàng)建或打開(kāi)一個(gè)互斥對(duì)象。CreateMutex()函數(shù)原型為:
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes, // 安全屬性指針
BOOL bInitialOwner, // 初始擁有者
LPCTSTR lpName // 互斥對(duì)象名
);
參數(shù)bInitialOwner主要用來(lái)控制互斥對(duì)象的初始狀態(tài)。一般多將其設(shè)置為FALSE,以表明互斥對(duì)象在創(chuàng)建時(shí)并沒(méi)有為任何線程所占有。如果在創(chuàng)建互斥對(duì)象時(shí)指定了對(duì)象名,那么可以在本進(jìn)程其他地方或是在其他進(jìn)程通過(guò)OpenMutex()函數(shù)得到此互斥對(duì)象的句柄。OpenMutex()函數(shù)原型為:
HANDLE OpenMutex(
DWORD dwDesiredAccess, // 訪問(wèn)標(biāo)志
BOOL bInheritHandle, // 繼承標(biāo)志
LPCTSTR lpName // 互斥對(duì)象名
);
當(dāng)目前對(duì)資源具有訪問(wèn)權(quán)的線程不再需要訪問(wèn)此資源而要離開(kāi)時(shí),必須通過(guò)ReleaseMutex()函數(shù)來(lái)釋放其擁有的互斥對(duì)象,其函數(shù)原型為:
BOOL ReleaseMutex(HANDLE hMutex);
其唯一的參數(shù)hMutex為待釋放的互斥對(duì)象句柄。至于WaitForSingleObject()和WaitForMultipleObjects()等待函數(shù)在互斥對(duì)象保持線程同步中所起的作用與在其他內(nèi)核對(duì)象中的作用是基本一致的,也是等待互斥內(nèi)核對(duì)象的通知。但是這里需要特別指出的是:在互斥對(duì)象通知引起調(diào)用等待函數(shù)返回時(shí),等待函數(shù)的返回值不再是通常的WAIT_OBJECT_0(對(duì)于WaitForSingleObject()函數(shù))或是在WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1之間的一個(gè)值(對(duì)于WaitForMultipleObjects()函數(shù)),而是將返回一個(gè)WAIT_ABANDONED_0(對(duì)于WaitForSingleObject()函數(shù))或是在WAIT_ABANDONED_0到WAIT_ABANDONED_0+nCount-1之間的一個(gè)值(對(duì)于WaitForMultipleObjects()函數(shù))。以此來(lái)表明線程正在等待的互斥對(duì)象由另外一個(gè)線程所擁有,而此線程卻在使用完共享資源前就已經(jīng)終止。除此之外,使用互斥對(duì)象的方法在等待線程的可調(diào)度性上同使用其他幾種內(nèi)核對(duì)象的方法也有所不同,其他內(nèi)核對(duì)象在沒(méi)有得到通知時(shí),受調(diào)用等待函數(shù)的作用,線程將會(huì)掛起,同時(shí)失去可調(diào)度性,而使用互斥的方法卻可以在等待的同時(shí)仍具有可調(diào)度性,這也正是互斥對(duì)象所能完成的非常規(guī)操作之一。
在編寫程序時(shí),互斥對(duì)象多用在對(duì)那些為多個(gè)線程所訪問(wèn)的內(nèi)存塊的保護(hù)上,可以確保任何線程在處理此內(nèi)存塊時(shí)都對(duì)其擁有可靠的獨(dú)占訪問(wèn)權(quán)。下面給出的示例代碼即通過(guò)互斥內(nèi)核對(duì)象hMutex對(duì)共享內(nèi)存快g_cArray[]進(jìn)行線程的獨(dú)占訪問(wèn)保護(hù)。下面給出實(shí)現(xiàn)代碼清單:
// 互斥對(duì)象
HANDLE hMutex = NULL;
char g_cArray[10];
UINT ThreadProc18(LPVOID pParam)
{
// 等待互斥對(duì)象通知
WaitForSingleObject(hMutex, INFINITE);
// 對(duì)共享資源進(jìn)行寫入操作
for (int i = 0; i < 10; i++)
{
g_cArray[i] = 'a';
Sleep(1);
}
// 釋放互斥對(duì)象
ReleaseMutex(hMutex);
return 0;
}
UINT ThreadProc19(LPVOID pParam)
{
// 等待互斥對(duì)象通知
WaitForSingleObject(hMutex, INFINITE);
// 對(duì)共享資源進(jìn)行寫入操作
for (int i = 0; i < 10; i++)
{
g_cArray[10 - i - 1] = 'b';
Sleep(1);
}
// 釋放互斥對(duì)象
ReleaseMutex(hMutex);
return 0;
}
……
void CSample08View::OnMutex()
{
// 創(chuàng)建互斥對(duì)象
hMutex = CreateMutex(NULL, FALSE, NULL);
// 啟動(dòng)線程
AfxBeginThread(ThreadProc18, NULL);
AfxBeginThread(ThreadProc19, NULL);
// 等待計(jì)算完畢
Sleep(300);
// 報(bào)告計(jì)算結(jié)果
CString sResult = CString(g_cArray);
AfxMessageBox(sResult);
}
互斥對(duì)象在MFC中通過(guò)CMutex類進(jìn)行表述。使用CMutex類的方法非常簡(jiǎn)單,在構(gòu)造CMutex類對(duì)象的同時(shí)可以指明待查詢的互斥對(duì)象的名字,在構(gòu)造函數(shù)返回后即可訪問(wèn)此互斥變量。CMutex類也是只含有構(gòu)造函數(shù)這唯一的成員函數(shù),當(dāng)完成對(duì)互斥對(duì)象保護(hù)資源的訪問(wèn)后,可通過(guò)調(diào)用從父類CSyncObject繼承的UnLock()函數(shù)完成對(duì)互斥對(duì)象的釋放。CMutex類構(gòu)造函數(shù)原型為:
CMutex( BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );
該類的適用范圍和實(shí)現(xiàn)原理與API方式創(chuàng)建的互斥內(nèi)核對(duì)象是完全類似的,但要簡(jiǎn)潔的多,下面給出就是對(duì)前面的示例代碼經(jīng)CMutex類改寫后的程序?qū)崿F(xiàn)清單:
// MFC互斥類對(duì)象
CMutex g_clsMutex(FALSE, NULL);
UINT ThreadProc27(LPVOID pParam)
{
// 等待互斥對(duì)象通知
g_clsMutex.Lock();
// 對(duì)共享資源進(jìn)行寫入操作
for (int i = 0; i < 10; i++)
{
g_cArray[i] = 'a';
Sleep(1);
}
// 釋放互斥對(duì)象
g_clsMutex.Unlock();
return 0;
}
UINT ThreadProc28(LPVOID pParam)
{
// 等待互斥對(duì)象通知
g_clsMutex.Lock();
// 對(duì)共享資源進(jìn)行寫入操作
for (int i = 0; i < 10; i++)
{
g_cArray[10 - i - 1] = 'b';
Sleep(1);
}
// 釋放互斥對(duì)象
g_clsMutex.Unlock();
return 0;
}
……
void CSample08View::OnMutexMfc()
{
// 啟動(dòng)線程
AfxBeginThread(ThreadProc27, NULL);
AfxBeginThread(ThreadProc28, NULL);
// 等待計(jì)算完畢
Sleep(300);
// 報(bào)告計(jì)算結(jié)果
CString sResult = CString(g_cArray);
AfxMessageBox(sResult);
}
小結(jié)
線程的使用使程序處理更夠更加靈活,而這種靈活同樣也會(huì)帶來(lái)各種不確定性的可能。尤其是在多個(gè)線程對(duì)同一公共變量進(jìn)行訪問(wèn)時(shí)。雖然未使用線程同步的程序代碼在邏輯上或許沒(méi)有什么問(wèn)題,但為了確保程序的正確、可靠運(yùn)行,必須在適當(dāng)?shù)膱?chǎng)合采取線程同步措施。
public class EagerSingleton {
String name;
private static final EagerSingleton m_instance = new EagerSingleton();
private EagerSingleton()
{
name ="wqh";
}
public static EagerSingleton getInstance()
{
return m_instance;
}
public static void main(String[] args) {
EagerSingleton es ;
es = EagerSingleton.getInstance();
System.out.println(es.name);
}
}
在有一種呢就是在該類型里面先聲明一個(gè)對(duì)象,如何通過(guò)一個(gè)同步的static方法每次返回同一個(gè) 對(duì)象,這也就是 所謂的懶漢模式,如下:
public class LazySingleton {
private static LazySingleton m_instance = null;
private LazySingleton()
{
}
synchronized public static LazySingleton getInstance()
{
if(m_instance == null)
{
m_instance = new LazySingleton();
}
return m_instance;
}
public static void main(String[] args) {
LazySingleton ls;
ls = LazySingleton.getInstance();
System.out.println(ls.getClass());
}
}
ArrayList 是由數(shù)組實(shí)現(xiàn)的 list , 他容許對(duì)元素進(jìn)行快速的隨機(jī)訪問(wèn),但是插入與移除 的速度很慢。ListIterator 只應(yīng)該用來(lái)從后往前 遍歷 ArrayList ,而不能夠做移除。
LinkedList 對(duì)順序訪問(wèn)進(jìn)行了優(yōu)化,向List 插入與移除的開(kāi)銷并不大,隨機(jī)訪問(wèn)則相對(duì)較慢。
Set (interface)存入Set 的每個(gè)元素都必須是唯一的,不保存重復(fù)元素。加入對(duì)象必須定義eqauls()方法保證唯一性。不保證維護(hù)元素的次序。
HsahSet 為快速查找而設(shè)計(jì)的 Set 存入HashSet的對(duì)象必須定義 hsahCode()方法
TreeSet 保持次序的Set,底層微樹結(jié)構(gòu),使用他可以從Set中提取有序的序列
LinkedHashSet 具有hashSet 的查詢速度,且內(nèi)部使用鏈表維護(hù)元素的順序(插入的次序)
Map(interface) 維護(hù)鍵值對(duì)的關(guān)聯(lián)性。
HashMap 基于散列表的實(shí)現(xiàn)(取代HashTable),插入和查詢鍵值對(duì)的開(kāi)銷固定,可以通過(guò)構(gòu)造器設(shè)置容量和負(fù)載因子以調(diào)整容器性能。
LinkedHashMap 類似HashMap ,但是疊帶遍歷時(shí)取得 鍵值對(duì) 的順序是插入的次序或者是 最近最少使用的次序。使用鏈表維護(hù)內(nèi)部次序。
TreeMap基于紅黑樹實(shí)現(xiàn),所得到的結(jié)果是經(jīng)過(guò)排序的。