posts - 29, comments - 0, trackbacks - 0, articles - 0
            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          (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 &


          
          



           


          posted @ 2007-06-08 09:54 change| 編輯 收藏

          測試網(wǎng)速的命令:

          ping -n 5 -l 1472 -f www.sina.com.cn


          LDAP:

          如果你把 ldap 安裝為 windows 服務(wù),你可以像我一樣啟動:

          net start OpenLDAP-slapd

          net stop OpenLDAP-slapd

          ldapadd -x -D "cn=Manager,dc=grid,dc=nwu" -W -f example.ldif

          ** 注意: 在第一次添加的時(shí)候要講默認(rèn)的庫 添加進(jìn)去才可以,否則會報(bào)錯誤 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

          將每一個目錄ENTRY看作是一個統(tǒng)一表的一行,然后把屬性看作列

          FAQ:
          1:
          windows下的openldap,filter 不能家單引號 如下(去掉單引號)
          ldapsearch -x -b dc=grid,dc=nwu (objectclass=*)

          2:
          用到的objectclass (處在schema中定義) 必須在 slapd.conf 中include進(jìn)來,否則報(bào)錯誤

          3:
          dn: cn=JSmith,ou=Managerment,dc=grid,dc=nwu
          cn: James Smith,Jim Smith,Jimmy Smith
          這里的dn中的cn  和屬性中的cn  必須是一樣的,否則報(bào)錯誤:alue of naming attribute 'cn' is not present in entry

          4:擴(kuò)展schema
          http://blog.csdn.net/wcy1323/archive/2006/04/26/678459.aspx

          posted @ 2007-05-28 15:56 change| 編輯 收藏


          ThreadLocal通過一個Map來為每個線程都持有一個變量副本。
          這個map以當(dāng)前線程為key。與synchronized相比,ThreadLocal是以空間換時(shí)間的策略來實(shí)現(xiàn)多線程程序。

          synchronized是利用鎖的機(jī)制,使變量或代碼塊在某一時(shí)該只能被一個線程訪問。
          而ThreadLocal為每一個線程都提供了變量的副本,使得每個線程在某一時(shí)間訪問到的并不是同一個對象,
          這樣就隔離了多個線程對數(shù)據(jù)的數(shù)據(jù)共享。
          而Synchronized卻正好相反,它用于在多個線程間通信時(shí)能夠獲得數(shù)據(jù)共享。
          Synchronized用于線程間的數(shù)據(jù)共享,而ThreadLocal則用于線程間的數(shù)據(jù)隔離。
          當(dāng)然ThreadLocal并不能替代synchronized,它們處理不同的問題域。
          Synchronized用于實(shí)現(xiàn)同步機(jī)制,比ThreadLocal更加復(fù)雜。

          posted @ 2007-05-28 15:53 change| 編輯 收藏

          from: :OReilly.JavaScript.The.Definitive.Guide.5th.Edition.Aug.2006

           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.

           

          posted @ 2007-05-28 15:50 change| 編輯 收藏

          java 是不直接支持 信號量的,我們必須自己來定義我們所需要的信號量

          class Semaphore {
          private int count;
          public Semaphore(int count) {
          this.count = count;
          }

          public synchronized void acquire() {
          while(count == 0) {
          try {
          wait();
          } catch (InterruptedException e) {
          //keep trying
          }
          }
          count--;
          }

          public synchronized void release() {
          count++;
          notify(); //alert a thread that´s blocking on this semaphore
          }
          }

          對要訪問的同步資源進(jìn)行 同步計(jì)數(shù)控制,來達(dá)到同步訪問資源的目的。

          posted @ 2007-05-28 15:49 change| 編輯 收藏

          線程死鎖條件:

          1:互斥條件 ,即資源是不能夠被共享的。

          2:至少有一個進(jìn)程在使用一個資源卻在等待另外一個線程所持有的一個資源

          3:資源部能夠被進(jìn)程搶占。

          4 :必須有循環(huán)的等待

          那么要解除死鎖,只要讓這幾個條件中的一個不成立就可以了。例如:

          哲學(xué)家問題,如果每個哲學(xué)家都是先 取左邊的筷子,在取右邊的筷子,那么很有可能就會出現(xiàn)死鎖了,那么我們可以讓最后的一個哲學(xué)家是先取右邊的筷子,然后再取左邊的筷子,那么就破壞了循環(huán)等待的原則,那么死鎖自然也就不會成立了 。當(dāng)然線程也可以一次獲得所有的所需資源來實(shí)現(xiàn)了。

          posted @ 2007-05-28 15:49 change| 編輯 收藏

           What does 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:

           

          1. The thread acquires the lock on the monitor for object this (assuming the monitor is unlocked, otherwise the thread waits until the monitor is unlocked).
          2. The thread memory flushes all its variables, i.e. it has all of its variables effectively read from "main" memory (JVMs can use dirty sets to optimize this so that only "dirty" variables are flushed, but conceptually this is the same. See section 17.9 of the Java language specification).
          3. The code block is executed (in this case setting the return value to the current value of i3, which may have just been reset from "main" memory).
          4. (Any changes to variables would normally now be written out to "main" memory, but for geti3() we have no changes.)
          5. The thread releases the lock on the monitor for object 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.

          posted @ 2007-05-28 15:48 change| 編輯 收藏

           
          Visual C++線程同步技術(shù)剖析:臨界區(qū),時(shí)間,信號量,互斥量
          xuefeifei 發(fā)表于 2006-1-12 10:43:00

          摘要: 多線程同步技術(shù)是計(jì)算機(jī)軟件開發(fā)的重要技術(shù),本文對多線程的各種同步技術(shù)的原理和實(shí)現(xiàn)進(jìn)行了初步探討。

          關(guān)鍵詞: VC++6.0; 線程同步;臨界區(qū);事件;互斥;信號量;

          正文

          使線程同步

            在程序中使用多線程時(shí),一般很少有多個線程能在其生命期內(nèi)進(jìn)行完全獨(dú)立的操作。更多的情況是一些線程進(jìn)行某些處理操作,而其他的線程必須對其處理結(jié)果進(jìn)行了解。正常情況下對這種處理結(jié)果的了解應(yīng)當(dāng)在其處理任務(wù)完成后進(jìn)行。

            如果不采取適當(dāng)?shù)拇胧?,其他線程往往會在線程處理任務(wù)結(jié)束前就去訪問處理結(jié)果,這就很有可能得到有關(guān)處理結(jié)果的錯誤了解。例如,多個線程同時(shí)訪問同一個全局變量,如果都是讀取操作,則不會出現(xiàn)問題。如果一個線程負(fù)責(zé)改變此變量的值,而其他線程負(fù)責(zé)同時(shí)讀取變量內(nèi)容,則不能保證讀取到的數(shù)據(jù)是經(jīng)過寫線程修改后的。

            為了確保讀線程讀取到的是經(jīng)過修改的變量,就必須在向變量寫入數(shù)據(jù)時(shí)禁止其他線程對其的任何訪問,直至賦值過程結(jié)束后再解除對其他線程的訪問限制。象這種保證線程能了解其他線程任務(wù)處理結(jié)束后的處理結(jié)果而采取的保護(hù)措施即為線程同步。

            線程同步是一個非常大的話題,包括方方面面的內(nèi)容。從大的方面講,線程的同步可分用戶模式的線程同步和內(nèi)核對象的線程同步兩大類。用戶模式中線程的同步方法主要有原子訪問和臨界區(qū)等方法。其特點(diǎn)是同步速度特別快,適合于對線程運(yùn)行速度有嚴(yán)格要求的場合。

            內(nèi)核對象的線程同步則主要由事件、等待定時(shí)器、信號量以及信號燈等內(nèi)核對象構(gòu)成。由于這種同步機(jī)制使用了內(nèi)核對象,使用時(shí)必須將線程從用戶模式切換到內(nèi)核模式,而這種轉(zhuǎn)換一般要耗費(fèi)近千個CPU周期,因此同步速度較慢,但在適用性上卻要遠(yuǎn)優(yōu)于用戶模式的線程同步方式。

          臨界區(qū)

            臨界區(qū)(Critical Section)是一段獨(dú)占對某些共享資源訪問的代碼,在任意時(shí)刻只允許一個線程對共享資源進(jìn)行訪問。如果有多個線程試圖同時(shí)訪問臨界區(qū),那么在有一個線程進(jìn)入后其他所有試圖訪問此臨界區(qū)的線程將被掛起,并一直持續(xù)到進(jìn)入臨界區(qū)的線程離開。臨界區(qū)在被釋放后,其他線程可以繼續(xù)搶占,并以此達(dá)到用原子方式操作共享資源的目的。

            臨界區(qū)在使用時(shí)以CRITICAL_SECTION結(jié)構(gòu)對象保護(hù)共享資源,并分別用EnterCriticalSection()和LeaveCriticalSection()函數(shù)去標(biāo)識和釋放一個臨界區(qū)。所用到的CRITICAL_SECTION結(jié)構(gòu)對象必須經(jīng)過InitializeCriticalSection()的初始化后才能使用,而且必須確保所有線程中的任何試圖訪問此共享資源的代碼都處在此臨界區(qū)的保護(hù)之下。否則臨界區(qū)將不會起到應(yīng)有的作用,共享資源依然有被破壞的可能。

          圖1 使用臨界區(qū)保持線程同步

          下面通過一段代碼展示了臨界區(qū)在保護(hù)多線程訪問的共享資源中的作用。通過兩個線程來分別對全局變量g_cArray[10]進(jìn)行寫入操作,用臨界區(qū)結(jié)構(gòu)對象g_cs來保持線程的同步,并在開啟線程前對其進(jìn)行初始化。為了使實(shí)驗(yàn)效果更加明顯,體現(xiàn)出臨界區(qū)的作用,在線程函數(shù)對共享資源g_cArray[10]的寫入時(shí),以Sleep()函數(shù)延遲1毫秒,使其他線程同其搶占CPU的可能性增大。如果不使用臨界區(qū)對其進(jìn)行保護(hù),則共享資源數(shù)據(jù)將被破壞(參見圖1(a)所示計(jì)算結(jié)果),而使用臨界區(qū)對線程保持同步后則可以得到正確的結(jié)果(參見圖1(b)所示計(jì)算結(jié)果)。代碼實(shí)現(xiàn)清單附下:

          // 臨界區(qū)結(jié)構(gòu)對象
          CRITICAL_SECTION g_cs;
          // 共享資源
          char g_cArray[10];
          UINT ThreadProc10(LPVOID pParam)
          {
           // 進(jìn)入臨界區(qū)
           EnterCriticalSection(&g_cs);
           // 對共享資源進(jìn)行寫入操作
           for (int i = 0; i < 10; i++)
           {
            g_cArray[i] = 'a';
            Sleep(1);
           }
           // 離開臨界區(qū)
           LeaveCriticalSection(&g_cs);
           return 0;
          }
          UINT ThreadProc11(LPVOID pParam)
          {
           // 進(jìn)入臨界區(qū)
           EnterCriticalSection(&g_cs);
           // 對共享資源進(jìn)行寫入操作
           for (int i = 0; i < 10; i++)
           {
            g_cArray[10 - i - 1] = 'b';
            Sleep(1);
           }
           // 離開臨界區(qū)
           LeaveCriticalSection(&g_cs);
           return 0;
          }
          ……
          void CSample08View::OnCriticalSection()
          {
           // 初始化臨界區(qū)
           InitializeCriticalSection(&g_cs);
           // 啟動線程
           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í)間過長,只要進(jìn)入臨界區(qū)的線程還沒有離開,其他所有試圖進(jìn)入此臨界區(qū)的線程都會被掛起而進(jìn)入到等待狀態(tài),并會在一定程度上影響。程序的運(yùn)行性能。尤其需要注意的是不要將等待用戶輸入或是其他一些外界干預(yù)的操作包含到臨界區(qū)。如果進(jìn)入了臨界區(qū)卻一直沒有釋放,同樣也會引起其他線程的長時(shí)間等待。換句話說,在執(zhí)行了EnterCriticalSection()語句進(jìn)入臨界區(qū)后無論發(fā)生什么,必須確保與之匹配的LeaveCriticalSection()都能夠被執(zhí)行到??梢酝ㄟ^添加結(jié)構(gòu)化異常處理代碼來確保LeaveCriticalSection()語句的執(zhí)行。雖然臨界區(qū)同步速度很快,但卻只能用來同步本進(jìn)程內(nèi)的線程,而不可用來同步多個進(jìn)程中的線程。

            MFC為臨界區(qū)提供有一個CCriticalSection類,使用該類進(jìn)行線程同步處理是非常簡單的,只需在線程函數(shù)中用CCriticalSection類成員函數(shù)Lock()和UnLock()標(biāo)定出被保護(hù)代碼片段即可。對于上述代碼,可通過CCriticalSection類將其改寫如下:

          // MFC臨界區(qū)類對象
          CCriticalSection g_clsCriticalSection;
          // 共享資源
          char g_cArray[10];
          UINT ThreadProc20(LPVOID pParam)
          {
           // 進(jìn)入臨界區(qū)
           g_clsCriticalSection.Lock();
           // 對共享資源進(jìn)行寫入操作
           for (int i = 0; i < 10; i++)
           {
            g_cArray[i] = 'a';
            Sleep(1);
           }
           // 離開臨界區(qū)
           g_clsCriticalSection.Unlock();
           return 0;
          }
          UINT ThreadProc21(LPVOID pParam)
          {
           // 進(jìn)入臨界區(qū)
           g_clsCriticalSection.Lock();
           // 對共享資源進(jìn)行寫入操作
           for (int i = 0; i < 10; i++)
           {
            g_cArray[10 - i - 1] = 'b';
            Sleep(1);
           }
           // 離開臨界區(qū)
           g_clsCriticalSection.Unlock();
           return 0;
          }
          ……
          void CSample08View::OnCriticalSectionMfc()
          {
           // 啟動線程
           AfxBeginThread(ThreadProc20, NULL);
           AfxBeginThread(ThreadProc21, NULL);
           // 等待計(jì)算完畢
           Sleep(300);
           // 報(bào)告計(jì)算結(jié)果
           CString sResult = CString(g_cArray);
           AfxMessageBox(sResult);
          }


          管理事件內(nèi)核對象

            在前面講述線程通信時(shí)曾使用過事件內(nèi)核對象來進(jìn)行線程間的通信,除此之外,事件內(nèi)核對象也可以通過通知操作的方式來保持線程的同步。對于前面那段使用臨界區(qū)保持線程同步的代碼可用事件對象的線程同步方法改寫如下:

          // 事件句柄
          HANDLE hEvent = NULL;
          // 共享資源
          char g_cArray[10];
          ……
          UINT ThreadProc12(LPVOID pParam)
          {
           // 等待事件置位
           WaitForSingleObject(hEvent, INFINITE);
           // 對共享資源進(jìn)行寫入操作
           for (int i = 0; i < 10; i++)
           {
            g_cArray[i] = 'a';
            Sleep(1);
           }
           // 處理完成后即將事件對象置位
           SetEvent(hEvent);
           return 0;
          }
          UINT ThreadProc13(LPVOID pParam)
          {
           // 等待事件置位
           WaitForSingleObject(hEvent, INFINITE);
           // 對共享資源進(jìn)行寫入操作
           for (int i = 0; i < 10; i++)
           {
            g_cArray[10 - i - 1] = 'b';
            Sleep(1);
           }
           // 處理完成后即將事件對象置位
           SetEvent(hEvent);
           return 0;
          }
          ……
          void CSample08View::OnEvent()
          {
           // 創(chuàng)建事件
           hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
           // 事件置位
           SetEvent(hEvent);
           // 啟動線程
           AfxBeginThread(ThreadProc12, NULL);
           AfxBeginThread(ThreadProc13, NULL);
           // 等待計(jì)算完畢
           Sleep(300);
           // 報(bào)告計(jì)算結(jié)果
           CString sResult = CString(g_cArray);
           AfxMessageBox(sResult);
          }


            在創(chuàng)建線程前,首先創(chuàng)建一個可以自動復(fù)位的事件內(nèi)核對象hEvent,而線程函數(shù)則通過WaitForSingleObject()等待函數(shù)無限等待hEvent的置位,只有在事件置位時(shí)WaitForSingleObject()才會返回,被保護(hù)的代碼將得以執(zhí)行。對于以自動復(fù)位方式創(chuàng)建的事件對象,在其置位后一被WaitForSingleObject()等待到就會立即復(fù)位,也就是說在執(zhí)行ThreadProc12()中的受保護(hù)代碼時(shí),事件對象已經(jīng)是復(fù)位狀態(tài)的,這時(shí)即使有ThreadProc13()對CPU的搶占,也會由于WaitForSingleObject()沒有hEvent的置位而不能繼續(xù)執(zhí)行,也就沒有可能破壞受保護(hù)的共享資源。在ThreadProc12()中的處理完成后可以通過SetEvent()對hEvent的置位而允許ThreadProc13()對共享資源g_cArray的處理。這里SetEvent()所起的作用可以看作是對某項(xiàng)特定任務(wù)完成的通知。

            使用臨界區(qū)只能同步同一進(jìn)程中的線程,而使用事件內(nèi)核對象則可以對進(jìn)程外的線程進(jìn)行同步,其前提是得到對此事件對象的訪問權(quán)??梢酝ㄟ^OpenEvent()函數(shù)獲取得到,其函數(shù)原型為:

          HANDLE OpenEvent(
           DWORD dwDesiredAccess, // 訪問標(biāo)志
           BOOL bInheritHandle, // 繼承標(biāo)志
           LPCTSTR lpName // 指向事件對象名的指針
          );


            如果事件對象已創(chuàng)建(在創(chuàng)建事件時(shí)需要指定事件名),函數(shù)將返回指定事件的句柄。對于那些在創(chuàng)建事件時(shí)沒有指定事件名的事件內(nèi)核對象,可以通過使用內(nèi)核對象的繼承性或是調(diào)用DuplicateHandle()函數(shù)來調(diào)用CreateEvent()以獲得對指定事件對象的訪問權(quán)。在獲取到訪問權(quán)后所進(jìn)行的同步操作與在同一個進(jìn)程中所進(jìn)行的線程同步操作是一樣的。

            如果需要在一個線程中等待多個事件,則用WaitForMultipleObjects()來等待。WaitForMultipleObjects()與WaitForSingleObject()類似,同時(shí)監(jiān)視位于句柄數(shù)組中的所有句柄。這些被監(jiān)視對象的句柄享有平等的優(yōu)先權(quán),任何一個句柄都不可能比其他句柄具有更高的優(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)核對象的數(shù)目,存放這些內(nèi)核對象的數(shù)組由lpHandles來指向。fWaitAll對指定的這nCount個內(nèi)核對象的兩種等待方式進(jìn)行了指定,為TRUE時(shí)當(dāng)所有對象都被通知時(shí)函數(shù)才會返回,為FALSE則只要其中任何一個得到通知就可以返回。dwMilliseconds在飫锏淖饔糜朐赪aitForSingleObject()中的作用是完全一致的。如果等待超時(shí),函數(shù)將返回WAIT_TIMEOUT。如果返回WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1中的某個值,則說明所有指定對象的狀態(tài)均為已通知狀態(tài)(當(dāng)fWaitAll為TRUE時(shí))或是用以減去WAIT_OBJECT_0而得到發(fā)生通知的對象的索引(當(dāng)fWaitAll為FALSE時(shí))。如果返回值在WAIT_ABANDONED_0與WAIT_ABANDONED_0+nCount-1之間,則表示所有指定對象的狀態(tài)均為已通知,且其中至少有一個對象是被丟棄的互斥對象(當(dāng)fWaitAll為TRUE時(shí)),或是用以減去WAIT_OBJECT_0表示一個等待正常結(jié)束的互斥對象的索引(當(dāng)fWaitAll為FALSE時(shí))。 下面給出的代碼主要展示了對WaitForMultipleObjects()函數(shù)的使用。通過對兩個事件內(nèi)核對象的等待來控制線程任務(wù)的執(zhí)行與中途退出:

          // 存放事件句柄的數(shù)組
          HANDLE hEvents[2];
          UINT ThreadProc14(LPVOID pParam)
          {
           // 等待開啟事件
           DWORD dwRet1 = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
           // 如果開啟事件到達(dá)則線程開始執(zhí)行任務(wù)
           if (dwRet1 == WAIT_OBJECT_0)
           {
            AfxMessageBox("線程開始工作!");
            while (true)
            {
             for (int i = 0; i < 10000; i++);
             // 在任務(wù)處理過程中等待結(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);
            // 開啟線程
            AfxBeginThread(ThreadProc14, NULL);
            // 設(shè)置事件0(開啟事件)
            SetEvent(hEvents[0]);
          }
          void CSample08View::OnEndevent()
          {
           // 設(shè)置事件1(結(jié)束事件)
           SetEvent(hEvents[1]);
          }


            MFC為事件相關(guān)處理也提供了一個CEvent類,共包含有除構(gòu)造函數(shù)外的4個成員函數(shù)PulseEvent()、ResetEvent()、SetEvent()和UnLock()。在功能上分別相當(dāng)與Win32 API的PulseEvent()、ResetEvent()、SetEvent()和CloseHandle()等函數(shù)。而構(gòu)造函數(shù)則履行了原CreateEvent()函數(shù)創(chuàng)建事件對象的職責(zé),其函數(shù)原型為:

          CEvent(BOOL bInitiallyOwn = FALSE, BOOL bManualReset = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );


            按照此缺省設(shè)置將創(chuàng)建一個自動復(fù)位、初始狀態(tài)為復(fù)位狀態(tài)的沒有名字的事件對象。封裝后的CEvent類使用起來更加方便,圖2即展示了CEvent類對A、B兩線程的同步過程:

          圖2 CEvent類對線程的同步過程示意

          B線程在執(zhí)行到CEvent類成員函數(shù)Lock()時(shí)將會發(fā)生阻塞,而A線程此時(shí)則可以在沒有B線程干擾的情況下對共享資源進(jìn)行處理,并在處理完成后通過成員函數(shù)SetEvent()向B發(fā)出事件,使其被釋放,得以對A先前已處理完畢的共享資源進(jìn)行操作??梢姡褂肅Event類對線程的同步方法與通過API函數(shù)進(jìn)行線程同步的處理方法是基本一致的。前面的API處理代碼可用CEvent類將其改寫為:

          // MFC事件類對象
          CEvent g_clsEvent;
          UINT ThreadProc22(LPVOID pParam)
          {
           // 對共享資源進(jìn)行寫入操作
           for (int i = 0; i < 10; i++)
           {
            g_cArray[i] = 'a';
            Sleep(1);
           }
           // 事件置位
           g_clsEvent.SetEvent();
           return 0;
          }
          UINT ThreadProc23(LPVOID pParam)
          {
           // 等待事件
           g_clsEvent.Lock();
           // 對共享資源進(jìn)行寫入操作
           for (int i = 0; i < 10; i++)
           {
            g_cArray[10 - i - 1] = 'b';
            Sleep(1);
           }
           return 0;
          }
          ……
          void CSample08View::OnEventMfc()
          {
           // 啟動線程
           AfxBeginThread(ThreadProc22, NULL);
           AfxBeginThread(ThreadProc23, NULL);
           // 等待計(jì)算完畢
           Sleep(300);
           // 報(bào)告計(jì)算結(jié)果
           CString sResult = CString(g_cArray);
           AfxMessageBox(sResult);
          }

           

            信號量內(nèi)核對象

            信號量(Semaphore)內(nèi)核對象對線程的同步方式與前面幾種方法不同,它允許多個線程在同一時(shí)刻訪問同一資源,但是需要限制在同一時(shí)刻訪問此資源的最大線程數(shù)目。在用CreateSemaphore()創(chuàng)建信號量時(shí)即要同時(shí)指出允許的最大資源計(jì)數(shù)和當(dāng)前可用資源計(jì)數(shù)。一般是將當(dāng)前可用資源計(jì)數(shù)設(shè)置為最大資源計(jì)數(shù),每增加一個線程對共享資源的訪問,當(dāng)前可用資源計(jì)數(shù)就會減1,只要當(dāng)前可用資源計(jì)數(shù)是大于0的,就可以發(fā)出信號量信號。但是當(dāng)前可用計(jì)數(shù)減小到0時(shí)則說明當(dāng)前占用資源的線程數(shù)已經(jīng)達(dá)到了所允許的最大數(shù)目,不能在允許其他線程的進(jìn)入,此時(shí)的信號量信號將無法發(fā)出。線程在處理完共享資源后,應(yīng)在離開的同時(shí)通過ReleaseSemaphore()函數(shù)將當(dāng)前可用資源計(jì)數(shù)加1。在任何時(shí)候當(dāng)前可用資源計(jì)數(shù)決不可能大于最大資源計(jì)數(shù)。

          圖3 使用信號量對象控制資源

          下面結(jié)合圖例3來演示信號量對象對資源的控制。在圖3中,以箭頭和白色箭頭表示共享資源所允許的最大資源計(jì)數(shù)和當(dāng)前可用資源計(jì)數(shù)。初始如圖(a)所示,最大資源計(jì)數(shù)和當(dāng)前可用資源計(jì)數(shù)均為4,此后每增加一個對資源進(jìn)行訪問的線程(用黑色箭頭表示)當(dāng)前資源計(jì)數(shù)就會相應(yīng)減1,圖(b)即表示的在3個線程對共享資源進(jìn)行訪問時(shí)的狀態(tài)。當(dāng)進(jìn)入線程數(shù)達(dá)到4個時(shí),將如圖(c)所示,此時(shí)已達(dá)到最大資源計(jì)數(shù),而當(dāng)前可用資源計(jì)數(shù)也已減到0,其他線程無法對共享資源進(jìn)行訪問。在當(dāng)前占有資源的線程處理完畢而退出后,將會釋放出空間,圖(d)已有兩個線程退出對資源的占有,當(dāng)前可用計(jì)數(shù)為2,可以再允許2個線程進(jìn)入到對資源的處理??梢钥闯?,信號量是通過計(jì)數(shù)來對線程訪問資源進(jìn)行控制的,而實(shí)際上信號量確實(shí)也被稱作Dijkstra計(jì)數(shù)器。

            使用信號量內(nèi)核對象進(jìn)行線程同步主要會用到CreateSemaphore()、OpenSemaphore()、ReleaseSemaphore()、WaitForSingleObject()和WaitForMultipleObjects()等函數(shù)。其中,CreateSemaphore()用來創(chuàng)建一個信號量內(nèi)核對象,其函數(shù)原型為:

          HANDLE CreateSemaphore(
           LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // 安全屬性指針
           LONG lInitialCount, // 初始計(jì)數(shù)
           LONG lMaximumCount, // 最大計(jì)數(shù)
           LPCTSTR lpName // 對象名指針
          );


            參數(shù)lMaximumCount是一個有符號32位值,定義了允許的最大資源計(jì)數(shù),最大取值不能超過4294967295。lpName參數(shù)可以為創(chuàng)建的信號量定義一個名字,由于其創(chuàng)建的是一個內(nèi)核對象,因此在其他進(jìn)程中可以通過該名字而得到此信號量。OpenSemaphore()函數(shù)即可用來根據(jù)信號量名打開在其他進(jìn)程中創(chuàng)建的信號量,函數(shù)原型如下:

          HANDLE OpenSemaphore(
           DWORD dwDesiredAccess, // 訪問標(biāo)志
           BOOL bInheritHandle, // 繼承標(biāo)志
           LPCTSTR lpName // 信號量名
          );


            在線程離開對共享資源的處理時(shí),必須通過ReleaseSemaphore()來增加當(dāng)前可用資源計(jì)數(shù)。否則將會出現(xiàn)當(dāng)前正在處理共享資源的實(shí)際線程數(shù)并沒有達(dá)到要限制的數(shù)值,而其他線程卻因?yàn)楫?dāng)前可用資源計(jì)數(shù)為0而仍無法進(jìn)入的情況。ReleaseSemaphore()的函數(shù)原型為:

          BOOL ReleaseSemaphore(
           HANDLE hSemaphore, // 信號量句柄
           LONG lReleaseCount, // 計(jì)數(shù)遞增數(shù)量
           LPLONG lpPreviousCount // 先前計(jì)數(shù)
          );


            該函數(shù)將lReleaseCount中的值添加給信號量的當(dāng)前資源計(jì)數(shù),一般將lReleaseCount設(shè)置為1,如果需要也可以設(shè)置其他的值。WaitForSingleObject()和WaitForMultipleObjects()主要用在試圖進(jìn)入共享資源的線程函數(shù)入口處,主要用來判斷信號量的當(dāng)前可用資源計(jì)數(shù)是否允許本線程的進(jìn)入。只有在當(dāng)前可用資源計(jì)數(shù)值大于0時(shí),被監(jiān)視的信號量內(nèi)核對象才會得到通知。

            信號量的使用特點(diǎn)使其更適用于對Socket(套接字)程序中線程的同步。例如,網(wǎng)絡(luò)上的HTTP服務(wù)器要對同一時(shí)間內(nèi)訪問同一頁面的用戶數(shù)加以限制,這時(shí)可以為沒一個用戶對服務(wù)器的頁面請求設(shè)置一個線程,而頁面則是待保護(hù)的共享資源,通過使用信號量對線程的同步作用可以確保在任一時(shí)刻無論有多少用戶對某一頁面進(jìn)行訪問,只有不大于設(shè)定的最大用戶數(shù)目的線程能夠進(jìn)行訪問,而其他的訪問企圖則被掛起,只有在有用戶退出對此頁面的訪問后才有可能進(jìn)入。下面給出的示例代碼即展示了類似的處理過程:

          // 信號量對象句柄
          HANDLE hSemaphore;
          UINT ThreadProc15(LPVOID pParam)
          {
           // 試圖進(jìn)入信號量關(guān)口
           WaitForSingleObject(hSemaphore, INFINITE);
           // 線程任務(wù)處理
           AfxMessageBox("線程一正在執(zhí)行!");
           // 釋放信號量計(jì)數(shù)
           ReleaseSemaphore(hSemaphore, 1, NULL);
           return 0;
          }
          UINT ThreadProc16(LPVOID pParam)
          {
           // 試圖進(jìn)入信號量關(guān)口
           WaitForSingleObject(hSemaphore, INFINITE);
           // 線程任務(wù)處理
           AfxMessageBox("線程二正在執(zhí)行!");
           // 釋放信號量計(jì)數(shù)
           ReleaseSemaphore(hSemaphore, 1, NULL);
           return 0;
          }
          UINT ThreadProc17(LPVOID pParam)
          {
           // 試圖進(jìn)入信號量關(guān)口
           WaitForSingleObject(hSemaphore, INFINITE);
           // 線程任務(wù)處理
           AfxMessageBox("線程三正在執(zhí)行!");
           // 釋放信號量計(jì)數(shù)
           ReleaseSemaphore(hSemaphore, 1, NULL);
           return 0;
          }
          ……
          void CSample08View::OnSemaphore()
          {
           // 創(chuàng)建信號量對象
           hSemaphore = CreateSemaphore(NULL, 2, 2, NULL);
           // 開啟線程
           AfxBeginThread(ThreadProc15, NULL);
           AfxBeginThread(ThreadProc16, NULL);
           AfxBeginThread(ThreadProc17, NULL);
          }


          圖4 開始進(jìn)入的兩個線程

          圖5 線程二退出后線程三才得以進(jìn)入

          上述代碼在開啟線程前首先創(chuàng)建了一個初始計(jì)數(shù)和最大資源計(jì)數(shù)均為2的信號量對象hSemaphore。即在同一時(shí)刻只允許2個線程進(jìn)入由hSemaphore保護(hù)的共享資源。隨后開啟的三個線程均試圖訪問此共享資源,在前兩個線程試圖訪問共享資源時(shí),由于hSemaphore的當(dāng)前可用資源計(jì)數(shù)分別為2和1,此時(shí)的hSemaphore是可以得到通知的,也就是說位于線程入口處的WaitForSingleObject()將立即返回,而在前兩個線程進(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é)果可以看出,信號量始終保持了同一時(shí)刻不超過2個線程的進(jìn)入。

            在MFC中,通過CSemaphore類對信號量作了表述。該類只具有一個構(gòu)造函數(shù),可以構(gòu)造一個信號量對象,并對初始資源計(jì)數(shù)、最大資源計(jì)數(shù)、對象名和安全屬性等進(jìn)行初始化,其原型如下:

          CSemaphore( LONG lInitialCount = 1, LONG lMaxCount = 1, LPCTSTR pstrName = NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL );


            在構(gòu)造了CSemaphore類對象后,任何一個訪問受保護(hù)共享資源的線程都必須通過CSemaphore從父類CSyncObject類繼承得到的Lock()和UnLock()成員函數(shù)來訪問或釋放CSemaphore對象。與前面介紹的幾種通過MFC類保持線程同步的方法類似,通過CSemaphore類也可以將前面的線程同步代碼進(jìn)行改寫,這兩種使用信號量的線程同步方法無論是在實(shí)現(xiàn)原理上還是從實(shí)現(xiàn)結(jié)果上都是完全一致的。下面給出經(jīng)MFC改寫后的信號量線程同步代碼:

          // MFC信號量類對象
          CSemaphore g_clsSemaphore(2, 2);
          UINT ThreadProc24(LPVOID pParam)
          {
           // 試圖進(jìn)入信號量關(guān)口
           g_clsSemaphore.Lock();
           // 線程任務(wù)處理
           AfxMessageBox("線程一正在執(zhí)行!");
           // 釋放信號量計(jì)數(shù)
           g_clsSemaphore.Unlock();
           return 0;
          }
          UINT ThreadProc25(LPVOID pParam)
          {
           // 試圖進(jìn)入信號量關(guān)口
           g_clsSemaphore.Lock();
           // 線程任務(wù)處理
           AfxMessageBox("線程二正在執(zhí)行!");
           // 釋放信號量計(jì)數(shù)
           g_clsSemaphore.Unlock();
           return 0;
          }
          UINT ThreadProc26(LPVOID pParam)
          {
           // 試圖進(jìn)入信號量關(guān)口
           g_clsSemaphore.Lock();
           // 線程任務(wù)處理
           AfxMessageBox("線程三正在執(zhí)行!");
           // 釋放信號量計(jì)數(shù)
           g_clsSemaphore.Unlock();
           return 0;
          }
          ……
          void CSample08View::OnSemaphoreMfc()
          {
           // 開啟線程
           AfxBeginThread(ThreadProc24, NULL);
           AfxBeginThread(ThreadProc25, NULL);
           AfxBeginThread(ThreadProc26, NULL);
          }

            互斥內(nèi)核對象

            互斥(Mutex)是一種用途非常廣泛的內(nèi)核對象。能夠保證多個線程對同一共享資源的互斥訪問。同臨界區(qū)有些類似,只有擁有互斥對象的線程才具有訪問資源的權(quán)限,由于互斥對象只有一個,因此就決定了任何情況下此共享資源都不會同時(shí)被多個線程所訪問。當(dāng)前占據(jù)資源的線程在任務(wù)處理完后應(yīng)將擁有的互斥對象交出,以便其他線程在獲得后得以訪問資源。與其他幾種內(nèi)核對象不同,互斥對象在操作系統(tǒng)中擁有特殊代碼,并由操作系統(tǒng)來管理,操作系統(tǒng)甚至還允許其進(jìn)行一些其他內(nèi)核對象所不能進(jìn)行的非常規(guī)操作。為便于理解,可參照圖6給出的互斥內(nèi)核對象的工作模型:

          圖6 使用互斥內(nèi)核對象對共享資源的保護(hù)
          圖(a)中的箭頭為要訪問資源(矩形框)的線程,但只有第二個線程擁有互斥對象(黑點(diǎn))并得以進(jìn)入到共享資源,而其他線程則會被排斥在外(如圖(b)所示)。當(dāng)此線程處理完共享資源并準(zhǔn)備離開此區(qū)域時(shí)將把其所擁有的互斥對象交出(如圖(c)所示),其他任何一個試圖訪問此資源的線程都有機(jī)會得到此互斥對象。

            以互斥內(nèi)核對象來保持線程同步可能用到的函數(shù)主要有CreateMutex()、OpenMutex()、ReleaseMutex()、WaitForSingleObject()和WaitForMultipleObjects()等。在使用互斥對象前,首先要通過CreateMutex()或OpenMutex()創(chuàng)建或打開一個互斥對象。CreateMutex()函數(shù)原型為:

          HANDLE CreateMutex(
           LPSECURITY_ATTRIBUTES lpMutexAttributes, // 安全屬性指針
           BOOL bInitialOwner, // 初始擁有者
           LPCTSTR lpName // 互斥對象名
          );

            參數(shù)bInitialOwner主要用來控制互斥對象的初始狀態(tài)。一般多將其設(shè)置為FALSE,以表明互斥對象在創(chuàng)建時(shí)并沒有為任何線程所占有。如果在創(chuàng)建互斥對象時(shí)指定了對象名,那么可以在本進(jìn)程其他地方或是在其他進(jìn)程通過OpenMutex()函數(shù)得到此互斥對象的句柄。OpenMutex()函數(shù)原型為:

          HANDLE OpenMutex(
           DWORD dwDesiredAccess, // 訪問標(biāo)志
           BOOL bInheritHandle, // 繼承標(biāo)志
           LPCTSTR lpName // 互斥對象名
          );

            當(dāng)目前對資源具有訪問權(quán)的線程不再需要訪問此資源而要離開時(shí),必須通過ReleaseMutex()函數(shù)來釋放其擁有的互斥對象,其函數(shù)原型為:

          BOOL ReleaseMutex(HANDLE hMutex);

            其唯一的參數(shù)hMutex為待釋放的互斥對象句柄。至于WaitForSingleObject()和WaitForMultipleObjects()等待函數(shù)在互斥對象保持線程同步中所起的作用與在其他內(nèi)核對象中的作用是基本一致的,也是等待互斥內(nèi)核對象的通知。但是這里需要特別指出的是:在互斥對象通知引起調(diào)用等待函數(shù)返回時(shí),等待函數(shù)的返回值不再是通常的WAIT_OBJECT_0(對于WaitForSingleObject()函數(shù))或是在WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1之間的一個值(對于WaitForMultipleObjects()函數(shù)),而是將返回一個WAIT_ABANDONED_0(對于WaitForSingleObject()函數(shù))或是在WAIT_ABANDONED_0到WAIT_ABANDONED_0+nCount-1之間的一個值(對于WaitForMultipleObjects()函數(shù))。以此來表明線程正在等待的互斥對象由另外一個線程所擁有,而此線程卻在使用完共享資源前就已經(jīng)終止。除此之外,使用互斥對象的方法在等待線程的可調(diào)度性上同使用其他幾種內(nèi)核對象的方法也有所不同,其他內(nèi)核對象在沒有得到通知時(shí),受調(diào)用等待函數(shù)的作用,線程將會掛起,同時(shí)失去可調(diào)度性,而使用互斥的方法卻可以在等待的同時(shí)仍具有可調(diào)度性,這也正是互斥對象所能完成的非常規(guī)操作之一。

            在編寫程序時(shí),互斥對象多用在對那些為多個線程所訪問的內(nèi)存塊的保護(hù)上,可以確保任何線程在處理此內(nèi)存塊時(shí)都對其擁有可靠的獨(dú)占訪問權(quán)。下面給出的示例代碼即通過互斥內(nèi)核對象hMutex對共享內(nèi)存快g_cArray[]進(jìn)行線程的獨(dú)占訪問保護(hù)。下面給出實(shí)現(xiàn)代碼清單:

          // 互斥對象
          HANDLE hMutex = NULL;
          char g_cArray[10];
          UINT ThreadProc18(LPVOID pParam)
          {
           // 等待互斥對象通知
           WaitForSingleObject(hMutex, INFINITE);
           // 對共享資源進(jìn)行寫入操作
           for (int i = 0; i < 10; i++)
           {
            g_cArray[i] = 'a';
            Sleep(1);
           }
           // 釋放互斥對象
           ReleaseMutex(hMutex);
           return 0;
          }
          UINT ThreadProc19(LPVOID pParam)
          {
           // 等待互斥對象通知
           WaitForSingleObject(hMutex, INFINITE);
           // 對共享資源進(jìn)行寫入操作
           for (int i = 0; i < 10; i++)
           {
            g_cArray[10 - i - 1] = 'b';
            Sleep(1);
           }
           // 釋放互斥對象
           ReleaseMutex(hMutex);
           return 0;
          }
          ……
          void CSample08View::OnMutex()
          {
           // 創(chuàng)建互斥對象
           hMutex = CreateMutex(NULL, FALSE, NULL);
           // 啟動線程
           AfxBeginThread(ThreadProc18, NULL);
           AfxBeginThread(ThreadProc19, NULL);
           // 等待計(jì)算完畢
           Sleep(300);
           // 報(bào)告計(jì)算結(jié)果
           CString sResult = CString(g_cArray);
           AfxMessageBox(sResult);
          }

            互斥對象在MFC中通過CMutex類進(jìn)行表述。使用CMutex類的方法非常簡單,在構(gòu)造CMutex類對象的同時(shí)可以指明待查詢的互斥對象的名字,在構(gòu)造函數(shù)返回后即可訪問此互斥變量。CMutex類也是只含有構(gòu)造函數(shù)這唯一的成員函數(shù),當(dāng)完成對互斥對象保護(hù)資源的訪問后,可通過調(diào)用從父類CSyncObject繼承的UnLock()函數(shù)完成對互斥對象的釋放。CMutex類構(gòu)造函數(shù)原型為:

          CMutex( BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );

            該類的適用范圍和實(shí)現(xiàn)原理與API方式創(chuàng)建的互斥內(nèi)核對象是完全類似的,但要簡潔的多,下面給出就是對前面的示例代碼經(jīng)CMutex類改寫后的程序?qū)崿F(xiàn)清單:

          // MFC互斥類對象
          CMutex g_clsMutex(FALSE, NULL);
          UINT ThreadProc27(LPVOID pParam)
          {
           // 等待互斥對象通知
           g_clsMutex.Lock();
           // 對共享資源進(jìn)行寫入操作
           for (int i = 0; i < 10; i++)
           {
            g_cArray[i] = 'a';
            Sleep(1);
           }
           // 釋放互斥對象
           g_clsMutex.Unlock();
           return 0;
          }
          UINT ThreadProc28(LPVOID pParam)
          {
           // 等待互斥對象通知
           g_clsMutex.Lock();
           // 對共享資源進(jìn)行寫入操作
           for (int i = 0; i < 10; i++)
           {
            g_cArray[10 - i - 1] = 'b';
            Sleep(1);
           }
           // 釋放互斥對象
           g_clsMutex.Unlock();
           return 0;
          }
          ……
          void CSample08View::OnMutexMfc()
          {
           // 啟動線程
           AfxBeginThread(ThreadProc27, NULL);
           AfxBeginThread(ThreadProc28, NULL);
           // 等待計(jì)算完畢
           Sleep(300);
           // 報(bào)告計(jì)算結(jié)果
           CString sResult = CString(g_cArray);
           AfxMessageBox(sResult);
          }

            小結(jié)

            線程的使用使程序處理更夠更加靈活,而這種靈活同樣也會帶來各種不確定性的可能。尤其是在多個線程對同一公共變量進(jìn)行訪問時(shí)。雖然未使用線程同步的程序代碼在邏輯上或許沒有什么問題,但為了確保程序的正確、可靠運(yùn)行,必須在適當(dāng)?shù)膱龊喜扇【€程同步措施。

          posted @ 2007-05-28 15:48 change| 編輯 收藏

          如何使一個類只能夠有一個對象 呢,我們知道這樣的類的構(gòu)造函數(shù)是必須為私有的,否則用戶就可以任意多的生產(chǎn)該類的對象了。那么具體應(yīng)該怎么實(shí)現(xiàn)呢,有兩種方式:一種是給類一個該類型的 static 成員對象,每次都調(diào)用一個get方法返回該對象,這也就是所謂的餓漢模式;

          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);
           }
          }

          在有一種呢就是在該類型里面先聲明一個對象,如何通過一個同步的static方法每次返回同一個 對象,這也就是 所謂的懶漢模式,如下:

          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());
           }
          }

            

          posted @ 2007-05-28 15:47 change| 編輯 收藏

          List (interface)  次序是List 的最重要的特點(diǎn)。它確保維護(hù)元素特定的順序。

          ArrayList 是由數(shù)組實(shí)現(xiàn)的 list , 他容許對元素進(jìn)行快速的隨機(jī)訪問,但是插入與移除 的速度很慢。ListIterator 只應(yīng)該用來從后往前 遍歷 ArrayList ,而不能夠做移除。

          LinkedList 對順序訪問進(jìn)行了優(yōu)化,向List 插入與移除的開銷并不大,隨機(jī)訪問則相對較慢。

          Set (interface)存入Set 的每個元素都必須是唯一的,不保存重復(fù)元素。加入對象必須定義eqauls()方法保證唯一性。不保證維護(hù)元素的次序。

          HsahSet 為快速查找而設(shè)計(jì)的 Set 存入HashSet的對象必須定義 hsahCode()方法

          TreeSet 保持次序的Set,底層微樹結(jié)構(gòu),使用他可以從Set中提取有序的序列

          LinkedHashSet 具有hashSet 的查詢速度,且內(nèi)部使用鏈表維護(hù)元素的順序(插入的次序)

          Map(interface) 維護(hù)鍵值對的關(guān)聯(lián)性。

          HashMap 基于散列表的實(shí)現(xiàn)(取代HashTable),插入和查詢鍵值對的開銷固定,可以通過構(gòu)造器設(shè)置容量和負(fù)載因子以調(diào)整容器性能。

          LinkedHashMap 類似HashMap ,但是疊帶遍歷時(shí)取得 鍵值對 的順序是插入的次序或者是 最近最少使用的次序。使用鏈表維護(hù)內(nèi)部次序。

          TreeMap基于紅黑樹實(shí)現(xiàn),所得到的結(jié)果是經(jīng)過排序的。

          posted @ 2007-05-28 15:46 change| 編輯 收藏

          僅列出標(biāo)題
          共3頁: 1 2 3 下一頁 
          主站蜘蛛池模板: 灵山县| 呼玛县| 宜黄县| 化德县| 万州区| 蕲春县| 武陟县| 邢台市| 雷山县| 收藏| 建阳市| 兴文县| 文安县| 祁门县| 岑溪市| 改则县| 陇南市| 普安县| 客服| 长顺县| 四川省| 姜堰市| 怀宁县| 广西| 客服| 南岸区| 永清县| 富民县| 淮安市| 华阴市| 丹阳市| 白朗县| 齐齐哈尔市| 鄂州市| 绥棱县| 吉安市| 广水市| 喜德县| 洛南县| 北川| 藁城市|