在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ C/ 總結
0x0E-單線程備份(下)
0x11-套接字編程-1
0x05-C語言指針:(Volume-1)
0x13-套接字編程-HTTP服務器(1)
0x0C-開始行動
C 語言進階
第一部分
0x05-C語言指針(Volume-2)
0x08-C語言效率(下)
0x07-C語言效率(上)
0x04 C代碼規(guī)范
0x0F-多線程備份
0x05-C語言變量
第四部分
0x16-套接字編程-HTTP服務器(4)
0x0D-單線程備份(上)
總結
0x01-C語言序言
0x15-套接字編程-HTTP服務器(3)
0x14-套接字編程-HTTP服務器(2)
0x17-套接字編程-HTTP服務器(5)
第三部分
我的C語言
0x06-C語言預處理器
0x09-未曾領略的新風景
0x0A-C線程和Glib的視角
第二部分
0x10-網絡的世界
0x12-套接字編程-2
0x03-C代碼
0x0B-C語言錯誤處理

總結

總結

  • 首先前面提到了一個思路:給隊列模型添加初步的線程保護,在使用它的時候,可以不考慮會保護其免受資源爭奪的問題

    • 實際上就是將CRITICAL_SECTION放在 Queue.c 的實現(xiàn)當中。
    • 讓兩個基本操作 PushBack PopFront 能夠自己實現(xiàn)保護自己
    • 具體應該怎么做呢?之前的我們對隊列模型中的 empty 實現(xiàn)了單獨保護,現(xiàn)在反過來,將其保護范圍擴大一些就行了。
  • 具體方法 Queue.c

    • 首先是取消使用 empty_sec 這個關鍵段/臨界區(qū)
    • 使用新的 static CRITICAL_SECTION io_section;
    • 修改 newQueuedel_queue 里的初始化和銷毀關鍵段代碼。
    • 以及重點的 push_backpop_front的代碼修改,前者變化多一些

          static int push_back(queue * __restrict object, const char * __restrict src, const char * __restrict dst)
          {
              char*    loc_src = NULL; /* 本地變量,盡量利用寄存器以及緩存 */
              char*    loc_dst = NULL;
              combine* loc_com = NULL;
              queue*   loc_que = object;
      
              size_t   len_src = strlen(src); /* 獲取路徑長度 */
              size_t   len_dst = strlen(dst);
              size_t   rear = 0; /* 隊列的隊尾 */
              size_t   front = 0; /* 隊列的隊首 */
      
              loc_src = Malloc_s(len_src + 1); /* 分配空間 */
              loc_dst = Malloc_s(len_dst + 1);
              loc_com = Malloc_s(sizeof(combine));
              if (loc_src == NULL || loc_dst == NULL || loc_com == NULL)
              {
                  Free_s(loc_src); /* 特殊處理過的釋放函數(shù) */
                  Free_s(loc_dst);
                  Free_s(loc_src);
                  return 1;
              }
              strcpy(loc_src, src); /* 構造路徑模型 */
              strcpy(loc_dst, dst);
              loc_com->dst_to_path = loc_dst;
              loc_com->src_from_path = loc_src;
              /* 進入保護 */
              EnterCriticalSection(&io_section); 
              rear = loc_que->rear;   /*獲取隊尾*/
              front = loc_que->front; /*獲取隊首*/
      
              loc_que->path_contain[rear++] = loc_com; /* 將本地路徑加入實體 */
              loc_que->rear = (rear % CAPCITY);     /* 用數(shù)組實現(xiàn)循環(huán)隊列的步驟 */
              /* 取消原先的保護 */
              if (loc_que->rear == loc_que->front)  
              {
                  loc_que->empty = 0;
              }
              LeaveCriticalSection(&io_section);
              return 0;
          }

      注釋里寫了很多信息,主要教之前的版本改變了一下串行代碼的順序,功能并沒有太大變化,變化的兩處地方,一個是內存分配錯誤判斷由三個if變成一個if,另一個是為了使臨界區(qū)內的代碼盡可能少,所以將一些操作移動了。

      `pop_front`代碼基本沒改變只是將臨界區(qū)擴大了保護范圍。
      
          static combine * pop_front(queue* object)
          {
              EnterCriticalSection(&io_section);
              size_t   loc_front = object->front;                   /*獲取當前隊首*/
              ... 
          //  EnterCriticalSection(&empty_sec); /* 原先的臨界區(qū)起始 */
              if (object->front == object->rear)
                  object->empty = 1;
              else
                  object->empty = 0;
          //  LeaveCriticalSection(&empty_sec);
              LeaveCriticalSection(&io_section);
              return loc_com;
          }   
  • 如此修改以后,該隊列模型就具備了初步的線程安全功能。
  • 在主代碼中,可以刪除 PopFrontPushBack 附近的保護操作。
  • 前方提到了,CRITICAL_SECTION 相對于 Mutex 不太安全,這里簡單說一下,具體請查詢相關資料
    • 前者只對當前代碼段負責,也就是其他操作這個資源的途徑是不被保護的。
    • 通俗的來說,假設有多個線程,CRITICAL_SECTION只保證在同一個"時間"內,只有一個線程能夠運行這段代碼,假設我在其他代碼還有對這段代碼中的資源進行訪問,那關鍵段就不能保證什么了。
    • 速度快開銷小是因為它和內核關系不大,是進程內的操作。
  • 發(fā)現(xiàn)問題了嗎?
    • 在代碼中,對于empty 的操作存在著問題,我們必須對它進行類似Mutex的保護,而不是使用CRITICAL_SECTION
    • 我們這里應該使用Mutex嗎?其實有一個更好的選擇,那就是在 Windows Vista之后引入的一個讀寫鎖SRWLOCK,允許多個線程讀取數(shù)據(jù)或者單個線程寫入數(shù)據(jù)
      • 為什么選擇它?道理還是一樣,因為它不使用內核資源。
      • 將代碼中對empty關鍵段保護修改或添加上SRWLOCK讀寫鎖的保護
      • 操作并沒有什么區(qū)別,就是進入保護區(qū)請求(AcquireSRWLock(Exclusive/Shared)),離開保護區(qū)釋放(ReleaseSRWLock(Exclusive/Shared))。
      • 本來有一個更好的可以減小開銷的 TryAcquire... 操作,但是確在Windows 7以后才引入,故不在此實現(xiàn)。

結后語

  • 所謂讀寫鎖,并不是所謂的銀彈,意思就是不要盲目的使用讀寫鎖,這里使用讀寫鎖只是因為想要更加全面的覆蓋知識點!
  • 要知道讀鎖的加持,并不一定就比一個互斥鎖(這是Linux平臺下對Windows臨界區(qū)的稱呼)要廉價,特別是在庫設計實現(xiàn)者手里,他必須考慮到種種因素,例如 寫鎖餓死 現(xiàn)象,想要解決這個問題,就不可避免的要犧牲讀鎖的性能,有可能將互斥鎖替換成讀寫鎖以后,性能反而降低了。

寫鎖餓死,是個挺廣泛的概念,只要有讀寫鎖的身影就必定有它,可以查閱相關資料

  • 這一切都是需要測試,測試,再測試,之所以選擇在 Windows 平臺上開發(fā),原因之一就是Visual Studio有一套強大到爆表的性能分析工具,你可以輕易找出代碼性能問題。善用它來研究不同鎖之間的差別,性能。

  • 對于多線程程序而言,同步原語實際上還是要善用 條件變量/互斥鎖(臨界區(qū),關鍵段)這兩個概念,能滿足90% 的需求,最多再使用一些平臺關鍵字就行了,不要參雜著各種各樣的同步魔法,要不就換一種思維,除了 多線程,并發(fā)世界還有許多“很美好”的東西

很美好。。。是我自己說的