forkjoinpool管理著forkjoinworkerthread執行緒,forkjoinworkerthread執行緒內部有乙個雙端佇列,這個雙端佇列主要由乙個陣列queue、陣列下標queuebase、陣列上標queuetop三個值保證。
forkjointask<?> queue:陣列的大小必須是2的n次方,方便將取模轉換為移位運算;
int queuetop:標識下乙個被push或者pop的位置,這個值只會被當前執行緒修改,因些沒有加volatile修飾;
volatile int queuebase:下乙個可以被其他執行緒steal的位置,由於其他執行緒會修改這個值,所以用volatile修飾保證可見性。
當向執行緒中新增任務時,有可能會導致陣列滿的情況,如下**所示:
final void pushtask(forkjointask<?> t)
}
其中s代表queuetop的值,m為陣列長度-1,當s == m時,也就是queue陣列中都放滿任務了,這時需要對陣列進行擴容。
private void growqueue()
}}
從以上擴容**可以看出,最大容量不能超過maximum_queue_capacity(1 << 24),最小不能小於初始值。每次擴容為先前大小的2倍,將原始陣列複製到新陣列中,同時將舊陣列置null。
擴容的過程中,queuebase和queuetop並不需要變化。
向執行緒佇列中新增乙個任務,或者向執行緒池新增乙個任務時,如果這個任務是乙個forkjointask例項,就會做入佇列的操作。前面已有這段**,這裡簡要分析一下
long u = (((s = queuetop) & (m = q.length - 1)) << ashift) + abase;
unsafe.putorderedobject(q, u, t);
queuetop = s + 1;
第一行,找到queuetop在陣列中的位置
第二行,用新任務填充queuetop所在位置
第三行,queuetop加1.
final void exectask(forkjointask<?> t)
++stealcount;
currentsteal = null;
}
注意locallyfifo 這個屬性,是否對自己的佇列採用fifo策略,預設為false,即預設從queuetop一端取任務。如果這個值為false,則從queuebase一端取資料。這個值可以通過forkjoinpool類的asyncmode屬性加以修改。
final forkjointask<?> locallydeqtask() }}
return null;
}private forkjointask<?> poptask() }}
return null;
}
關鍵兩句話:
queuebase = b + 1:fifo策略每次從queuebase取任務,每取乙個,queuebase增加1;
--s,queuetop = s:lifo策略每次從queuetop取任務,每取乙個,queuetop減1。
以下是work-stealing的核心**
for (;;)
}
1、瞄到第i個位置這個任務,i = (q.length-1) & b,i其實就是queuebase在陣列中所在的位置;
2、將這個位置上的任務設定為null,並增加queuebase的值,設定stealhint表示你的東西被我偷了;
3、儲存先前的currentsteal值,設定currentsteal為這個偷來的task,然後執行這個task,執行完後,恢復currentsteal的值。
STL之雙端佇列
deque雙向佇列是一種雙向開口的連續 線性空間 可以高效的在頭尾兩端插入和刪除元素,deque在介面上和 vector 非常相似,下面列出deque的常用成員函式 deque.c c.assgin beg,end c.assgin n,elem c.at idx c.front c.back c....
C STl之雙端佇列
deque與vector非常相似,不僅可以在尾部插入和刪除元素,還可以在頭部插入和刪除。不過當考慮到容器元素的記憶體分配策略和操作效能時,deque相對vector較為有優勢。標頭檔案 include 1 deque 建立乙個沒有任何元素的deque物件。deque d 2 deque size t...
佇列 雙端佇列
1.佇列 佇列是遵循先進先出 fifo,也稱為先來先服務 原則的一組有序的項。佇列在尾部新增新 元素,並從頂部移除元素。最新新增的元素必須排在佇列的末尾 class queue 向佇列新增元素 enqueue element 檢查佇列是否為空並獲取它的長度 isempty 從佇列移除元素 deque...