Preface
如果有一個資料檔有可能同時間會有許多客戶端對它進行讀取與寫入的動作,則必須注意資料的同步問題,像是兩個寫入者進行寫入時,後一個寫入者的資料會有可 能將次一個寫入者的資料覆蓋掉;而有時您希望讀取者看到的是最新的資料,如果在讀取的時候,有寫入者想要對資料進行寫入,則最好等待讀取者讀取完畢,相反 的如果在寫入時有客戶想要讀取資料,則最好等待,以確保讀出來的資料是最新的資料。
讀取寫入的同步問題向來是難解的問題之一,有幾個可行的作法,例如若有寫入的動作時,則讀取者以唯讀模式開啟;或是如果有開啟資料檔的動作時,無論是讀取 或是寫入,後一個開啟檔案的客戶都一律以唯讀模式開啟;還有最乾脆的作法,就是將這個問題由客戶決定,在開啟檔案時若已有其他人開啟中,則提供選項讓客戶 決定要不要以唯讀模式開啟,通常這個作法是提供給檔案的擁有者使用。
Read-Write-Lock 模式
Read-Write-Lock 模式提供給被讀取或寫入的資料一個鎖定物件,在讀取或寫入時要向鎖定物件形式上讀取鎖定,實際上真正是否鎖定共用資源,由鎖定物件來判斷。一個簡單的 Scala 程式片段如下所示:
- def readData(lock:Lock)={
- lock.readLock();
- doRead();
- lock.readUnLock();
- }
- def writeData(lock:Lock)={
- lock.writeLock()
- doWrite()
- lock.writeUnLock()
- }
如果可以同時讀取,現在假設有個讀取者已經取得鎖,另一個讀取者其實也還是可以如下形式上取得鎖定並讀取。如果現在只剩一個讀取者,而寫入者試圖進行寫入,它也試圖先取得鎖定,但發現鎖已經被讀取的一方擁有,於是先進入等待,直到讀取的一方解除鎖定為止:
而最主要的關鍵還是在於鎖的實現,在 Java/Scala 中可以用 wait()、notify() 來實現,實現的片段如下:
- Lock.scala
- package dp.thread.rwlock
- class Lock(val writerFirst:Boolean = true,
- var writingWriters:Int = 0,
- var waitingWriters:Int = 0,
- var readingReaders:Int = 0) {
- def readLock()={
- this.synchronized
- {
- while(writingWriters > 0 || (writerFirst && waitingWriters > 0)) {
- wait();
- }
- readingReaders+=1
- }
- }
- def readUnLock()={
- this.synchronized{
- readingReaders-=1
- notifyAll()
- }
- }
- def writeLock()={
- this.synchronized{
- waitingWriters+=1
- try{
- while(readingReaders > 0 || writingWriters > 0) {
- wait();
- }
- } catch {
- case _:Throwable => printf("\t[Warn] Exception while wait for write lock!\n")
- }
- finally{
- waitingWriters-=1
- }
- writingWriters+=1
- }
- }
- def writeUnLock()={
- this.synchronized{
- writingWriters-=1;
- notifyAll();
- }
- }
- }
- RWOp.scala
- package dp.thread.rwlock
- trait RWOp {
- def doRead()
- def doWrite(line:String)
- def readData(lock:Lock)={
- lock.readLock();
- doRead();
- lock.readUnLock();
- }
- def writeData(lock:Lock, line:String)={
- lock.writeLock()
- doWrite(line)
- lock.writeUnLock()
- }
- }
- package dp.thread.rwlock
- import java.util.Random
- object Main extends App {
- class TxtFile(var cnt:String="") extends RWOp{
- def doRead() = {printf("(%d) Read Text file:\n%s\n", Thread.currentThread().getId(), cnt)}
- def doWrite(line:String) = {printf("(%d) Write Text file: %s...\n", Thread.currentThread().getId(), line); cnt = cnt+line}
- }
- val txtFile = new TxtFile()
- val lock = new Lock()
- val rdm = new Random()
- var lineNum:Int=0
- def acquireLineNum():Int=
- {
- Main.synchronized{
- lineNum+=1
- return lineNum
- }
- }
- for(i <- nbsp="" span="">1 to 5) ->
- {
- new Thread(){
- override def run()=
- {
- for(j <- nbsp="" span="">1 to 5) ->
- {
- val ri = rdm.nextInt(10)
- if(ri<=3)
- {
- txtFile.readData(lock)
- }
- else
- {
- txtFile.writeData(lock, acquireLineNum+" ")
- }
- }
- }
- }.start()
- }
- }
* FAQ - How to get thread id from a thread pool?
沒有留言:
張貼留言