日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

高并发编程-重排序

發(fā)布時間:2025/3/21 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 高并发编程-重排序 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

  • 定義
  • 數(shù)據(jù)依賴性
  • as-if-serial語義
  • 程序順序規(guī)則
  • 重排序?qū)Χ嗑€程的影響

定義

重排序是指編譯器處理器為了優(yōu)化程序性能而對指令序列進行重新排序的一種手段。


數(shù)據(jù)依賴性

如果兩個操作訪問同一個變量,且這兩個操作中有一個為寫操作,此時這兩個操作之間就存在數(shù)據(jù)依賴性.

名稱代碼說明
寫后讀a=1;b=a;寫一個變量后,再讀這個位置
寫后寫a=1;a=2寫一個變量后,再寫這個變量
讀后寫a=b;b=1;讀一個變量后,再寫這個變量

上面3種情況,只要重排序兩個操作的執(zhí)行順序,程序的執(zhí)行結(jié)果就會被改變。

前面提到過,編譯器和處理器可能會對操作做重排序。編譯器和處理器在重排序時,會遵守數(shù)據(jù)依賴性,編譯器和處理器不會改變存在數(shù)據(jù)依賴關(guān)系的兩個操作的執(zhí)行順序。

這里所說的數(shù)據(jù)依賴性僅針對單個處理器中執(zhí)行的指令序列和單個線程中執(zhí)行的操作,不同處理器之間和不同線程之間的數(shù)據(jù)依賴性不被編譯器和處理器考慮。


as-if-serial語義

as-if-serial語義的意思是:不管怎么重排序(編譯器和處理器為了提高并行度),(單線程)程序的執(zhí)行結(jié)果不能被改變。

編譯器、runtime和處理器都必須遵守as-if-serial語義。

為了遵守as-if-serial語義,編譯器和處理器不會對存在數(shù)據(jù)依賴關(guān)系的操作做重排序,因為這種重排序會改變執(zhí)行結(jié)果。

但是,如果操作之間不存在數(shù)據(jù)依賴關(guān)系,這些操作就可能被編譯器和處理器重排序。

舉個例子 : 計算圓面積

double pi = 3.14; // A double r = 1.0; // B double area = pi * r * r; // C

上面3個操作的數(shù)據(jù)依賴關(guān)系如下所示

A和C之間存在數(shù)據(jù)依賴關(guān)系,同時B和C之間也存在數(shù)據(jù)依賴關(guān)系。因此最終執(zhí)行的指令序列中,C不能被重排序到A和B的前面(C排到A和B的前面,程序的結(jié)果將會被改變)。

但A和B之間沒有數(shù)據(jù)依賴關(guān)系,編譯器和處理器可以重排序A和B之間的執(zhí)行順序。

as-if-serial語義把單線程程序保護了起來,遵守as-if-serial語義的編譯器、runtime和處理器共同為編寫單線程程序的程序員創(chuàng)建了一個幻覺:單線程程序是按程序的順序來執(zhí)行的。as-if-serial語義使單線程程序員無需擔(dān)心重排序會干擾他們,也無需擔(dān)心內(nèi)存可見性問題。


程序順序規(guī)則

根據(jù)happens-before的程序順序規(guī)則,上面計算圓的面積的示例代碼存在3個happens-before關(guān)系。

1)A happens-before B。 2)B happens-before C。 3)A happens-before C。

這里的第3個happens-before關(guān)系,是根據(jù)happens-before的傳遞性推導(dǎo)出來的。


重排序?qū)Χ嗑€程的影響

我們來看看,重排序是否會改變多線程程序的執(zhí)行結(jié)果。 請看下面的示例代碼

public class AsIfSerial {private int a = 0;private boolean flag = false;public void wirte() {a = 1; // 操作1flag = true;// 操作2System.out.println(Thread.currentThread().getName() + " 更新后 a=" + a + " , flag=" + flag);}public void read() {System.out.println(Thread.currentThread().getName() + " 讀取值 a=" + a + " , flag=" + flag);if (flag) { // 操作3 int i = a * a; // 操作4System.out.println(Thread.currentThread().getName() + " 執(zhí)行結(jié)果:" + i);}}}

flag變量是個標記,用來標識變量a是否已被寫入。這里假設(shè)有兩個線程A和B,A首先執(zhí)行writer()方法,隨后B線程接著執(zhí)行reader()方法。線程B在執(zhí)行操作4時,能否看到線程A在操作1對共享變量a的寫入呢? ---------->不一定能看到.

由于操作1和操作2沒有數(shù)據(jù)依賴關(guān)系,編譯器和處理器可以對這兩個操作重排序;同樣操作3和操作4沒有數(shù)據(jù)依賴關(guān)系,編譯器和處理器也可以對這兩個操作重排序。

讓我們先來看看,當操作1和操作2重排序時,可能會產(chǎn)生什么效果?

操作1和操作2做了重排序。程序執(zhí)行時,線程A首先寫標記變量flag,隨后線程B讀這個變量。由于條件判斷為真,線程B將讀取變量a。此時,變量a還沒有被線程A寫入,在這里多線程程序的語義被重排序破壞了! (虛箭線標識錯誤的讀操作)

再讓我們看看,當操作3和操作4重排序時會產(chǎn)生什么效果(借助這個重排序,可以順便說明控制依賴性)。下面是操作3和操作4重排序后,程序執(zhí)行的時序圖

在程序中,操作3和操作4存在控制依賴關(guān)系。當代碼中存在控制依賴性時,會影響指令序列執(zhí)行的并行度。

為此,編譯器和處理器會采用猜測(Speculation)執(zhí)行來克服控制相關(guān)性對并行度的影響。以處理器的猜測執(zhí)行為例,執(zhí)行線程B的處理器可以提前讀取并計算a*a,然后把計算結(jié)果臨時保存到一個名為重排序緩沖(Reorder Buffer,ROB)的硬件緩存中。當操作3的條件判斷為真時,就把該計算結(jié)果寫入變量i中。

從上圖中我們可以看出,猜測執(zhí)行實質(zhì)上對操作3和4做了重排序。重排序在這里破壞了多線程程序的語義!

在單線程程序中,對存在控制依賴的操作重排序,不會改變執(zhí)行結(jié)果(這也是as-if-serial語義允許對存在控制依賴的操作做重排序的原因);但在多線程程序中,對存在控制依賴的操作重排序,可能會改變程序的執(zhí)行結(jié)果。

咋改呢 ? 加 volatile

package com.artisan.test;public class AsIfSerial {// 共享變量: 實例域 (同一個對象的)private volatile int a = 0;// 共享變量: 實例域(同一個對象的)private volatile boolean flag = false;public void wirte() {a = 1;flag = true;System.out.println(Thread.currentThread().getName() + " 更新后 a=" + a + " , flag=" + flag);}public void read() {System.out.println(Thread.currentThread().getName() + " 讀取值 a=" + a + " , flag=" + flag);if (flag) {int i = a * a;System.out.println(Thread.currentThread().getName() + " 執(zhí)行結(jié)果:" + i);}}public static void main(String[] args) throws InterruptedException {// 實例化出來一個對象AsIfSerial asIfSerial = new AsIfSerial();new Thread(() -> {asIfSerial.wirte();}, "WRITE").start();// sleep一下 確保 WRITE線程先啟動Thread.sleep(500);new Thread(() -> {asIfSerial.read();}, "READ").start();}}

總結(jié)

以上是生活随笔為你收集整理的高并发编程-重排序的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。