跳到內容
關於我 數位花園

部落格

[開箱] Boox Poke 4 Lite 電子閱讀器:開放與封閉的抉擇

最近入手了 Boox 文石的電子閱讀器 Poke 4 Lite,今天就來聊一下關於電子閱讀器的各種事吧!😎

說到電子書,亞馬遜可以說是很早就在佈局電子書市場,深耕多年。

在偶然的情況下,從友人那裡獲得了一台 Kindle Paperwhite 第二代,開啟了閱讀電子書的旅程。

一開始使用 Kindle 的印象,沒有 LED 螢幕的背光、沒有惱人的 APP 訊息通知、因為很省電,只需要好幾周充一次電,各種會打擾分心的因素幾乎都摒除了,打造了可以靜心讀書的環境。

使用 Kindle 閱讀器,除了在 Kindle 書城買書(我是使用日本亞馬遜),也會自己在網路上下載電子書來看。由於 Kindle 可接受的檔案格式是 MOBI、AWZ,較為通行的電子書格式 EPUB 卻無法相容,因此下載下來的電子書檔案都要經過轉檔。另外,若是下載了簡體書,也要轉換成繁體的版本,增加了不少麻煩,畢竟 Kindle 是封閉系統的閱讀器。但所幸轉檔、置入檔案的流程不是非常頻繁,書也不是很快就看完,所以也不算是太大的困擾。

有好一陣子,當時(約莫是 2014 年)的日本亞馬遜書城裡沒有繁體中文書(大概有簡體書),而且不想換到中國亞馬遜看簡體書,也沒有很積極地要閱讀英、日文的書籍(想練習外語閱讀力,但是懶惰 🙈),反而都去圖書館借中文書來看,不用錢又有看不完的書,實在是很大的吸引力,因此 Kindle 閱讀器就晾在一旁,而且一放就是好幾年。2016 年,正是台灣電子書剛起步的時候,我也開始在用樂天 kobo 買電子書,通勤的時候,在手機(四吋的 iPhone SE)上閱讀,後來也在讀墨 Readmoo 上也買了一些電子書。總之,用手機看電子書的機會增加了許多,畢竟出門的時候一定會帶手機,然而閱讀器卻未必。「想要看卻發現沒帶身上,帶出門卻沒有用」的情況也不在少數(墨菲定律啊)。由於使用的頻率越來越少了,於是在某一次整理東西的時候,毅然決定將 Kindle 給脫手了。

去圖書館借書,的確十分方便的資源,而且也會不斷有新書可以借閱。以台北市的圖書館為例,以往個人借閱證的借閱額度是 10 本,現在好像增加到 25 本的樣子,利用網頁或 APP 預約借書,可以在預約書都抵達的時候一次借回來,然後看完之後,再換其他本。比較熱門的書通常預約的人比較多,會等久一點,不過除非是閱讀迅速的人,不然應該不受影響,畢竟大部分的書是不需要排隊等待借書的。而且,圖書館就是滿滿的書山,根本看不完哪~

我聽說有些人比較潔癖,因為嫌圖書館的書太髒,所以從不借圖書館的書。圖書館的書畢竟是很多人使用過的,確實不免會比較髒,我雖然沒有非常在意,但有時候也會遇到很不好的情況,例如:紙上有原子筆、鉛筆的畫記,黏著鼻屎、或將整頁插畫用美工刀割下來的情況,實在是挺困擾的。有免費的書讓很多人可以閱讀,卻做如此缺德的事,實在是不可取啊 😔

此外,有些熱門書,就算要排隊預約,也要排幾個月,看到自己排一、兩千號,也是挺灰心的。像是前陣子很熱門的「被討厭的勇氣」、「情緒勒索」、「原子習慣」,都是大排長龍的熱門書。但想一想,一本書也沒多少錢,買了就可以馬上享受了,就算可以免費看,卻要等足足半年之久,感覺也不划算啊~而這也是我會直接買電子書的一大動機。

近年,日本亞馬遜書城上開始有繁體中文書了。雖然書量跟台灣樂天 kobo 與讀墨 Readmoo 兩大巨頭比起來還差很遠就是了。然而日本亞馬遜開始上架繁體中文書這個趨勢,著實令人期待。因為以電子書的 APP 整體使用體驗而言,我認為 Kindle 還是最好的。

首先,先來談一下書城的平台,也就是軟體的部分。

電子書綁定書城的平台及其帳號,若我們在各家都買了書,就會形成很分散的情況:想像一下,家中必須放很多個書櫃,大本的書必須放大的書櫃、一般的書籍放在一般的書櫃、文庫本的書籍則放在小型的書櫃、而漫畫尺寸也放在漫畫尺寸的書櫃。

若我們在各個平台都買了幾本書(通常都是因為想讀的書在 A 書城正好特價,或是只有 B 書城有買電子書的版權),就會發生這樣的情況,手機裡安裝了各家的 APP,我現在就是處於這樣的混亂狀態。若要集中管理,只能在固定在某一家買書了。電子書商城的經營模式畢竟不一樣,無法像紙本書一樣,想買皇冠出版的哈利波特時,誠品書店有賣、金石堂書店也有賣、在博客來網路書店也買得到。

或許有人會說:「電子書的優點就是輕便啊,多裝一個 APP 在手機裡也不會變重,沒有這麼嚴重吧」

我可以說,或許這是身為極簡主義者的潔癖情節 😝

除了書籍綁定書城平台之外,再來談談硬體:閱讀器的部分。

由於各家的閱讀器幾乎都是封閉式系統,意思就是:看書只能用自己家的書城。若我們買樂天 kobo 的閱讀器,就只能看 kobo 的書。當然自己放電子書檔案進去通常也是允許的,但畢竟那不是花錢買授權書的流程,而不在討論的範圍內。所以,若有使用好幾個書城,且都要使用閱讀器的話,豈不是要買好幾個閱讀器嗎?!這實在與電子書環保的理念背道而馳,節省了紙張的消費,反而買了更多電子產品,我想大部分人也不會這麼做。因此,開放系統的閱讀器正可以解決這個問題。

Boox 文石的閱讀器在代理進台灣之後,我一直有在關注。他們主打的一大賣點就是:Android 系統的閱讀器,所以可在 Play 商店下載任何 APP,就跟使用智慧型手機一樣。開放系統的優點,解決了封閉系統閱讀器的最大痛點,也就是單裝置多平台的終極目標。然而,文石的閱讀器也不是沒有缺點。

電子墨水螢幕跟手機螢幕的差別在於,基於目前(2022 年)電子墨水的技術,它的刷新速度沒有 LED(或 OLED)螢幕這麼快速,因此一般的手機 APP 也不會特別為了電子墨水螢幕這個小眾硬體去做軟體上的優化或客製化的設定。而這些正是封閉系統閱讀器的優點所在。封閉系統的閱讀器針對電子墨水螢幕的特性去做軟體上的調校,所以在使用體驗會比手機 APP 直接在電子墨水螢幕上好很多。例如 APP 通常會加上翻頁的特效,因為這樣的特效在 LED 上很有翻實體書的感覺,LED 螢幕上看起來演示得很好,但在刷新速度較低的電子墨水螢幕上卻慘不忍睹。有些 APP 可以在設定裡面,將一些漸變動畫給關閉,再翻頁切換的體驗就會近似於封閉系統的閱讀器,但有些則不行,例如說亞馬遜的 Kindle APP ⋯⋯

於是,最近因為有開始閱讀電子書,手機閱讀縱然方便,但因為工作時都是使用電腦,所以希望下班後可以減少看 LED 螢幕的時間,於是又興起了買電子閱讀器的念頭。

雖然看到亞馬遜可以買繁體中文書有點心動,很想購入最新的 Kindle 閱讀器,但礙於手邊還有其他平台的中文書籍要閱讀,因而頓時陷入了兩難。最後的抉擇是買了文石 Boox Poke 4 Lite,在上面閱讀 Kindle、kobo、Readmoo 的電子書。

標題雖然稱之為開箱文,但沒有常見開箱文該有的樣子,請見諒 😅

這裡就簡單列一下 Boox Poke 4 Lite 的規格,並與低、中階最新的 Kindle、還有我的手機做比較:

Poke 4 LiteKindle 11Kindle Paperwhite 5iPhone 12 mini
尺寸153 x 107 x 7.1 mm158 x 109 x 8.0 mm180 x 120 x 8.1 mm131.5 x 64.2 x 7.4 mm
重量150g158g205g135g
顯示器6 吋6 吋6.8 吋5.4 吋
解析度758 x 1024
(212 ppi)
1072 × 1448
(300ppi)
1236 x 1648
(300ppi)
1080 x 2340
(476ppi)

一開始對 Poke 212ppi 的解析度有點擔心,但使用幾天下來,發現也看不出什麼顆粒感(說到底沒有寫輪眼),150 克的重量拿起來真的沒什麼負擔。若說要以集中使用亞馬遜買電子書為目標來說(念念不忘 Kindle 😝),Boox 算是一個不錯的過渡期選擇,當然或許就此習慣開放系統的閱讀器而回不去了也說不定。總之,比起用手機閱讀,電子墨水的螢幕總是舒服許多,重點是毫無分心、毫無干擾。

或許有人會問:「既然是開放式系統,會不會因此把手機常用的 APP 都安裝,然後就跟手機一樣充滿著推播訊息?」

我有試著裝過 Spotify,操作體驗真的不好。前面有提過,電子墨水目前的更新速度並不快速,就算裝了社群類、通訊類 APP,我們也不會想要在上面使用,因為體驗實在是太糟了 😆

既然說到這裡,來分享一下我目前有裝的 APP 吧:

  • Kindle
  • Notion
  • Readmoo 看書
  • Wikipedia
  • 樂天 Kobo

雖然本篇主要是想聊聊電子閱讀器,畢竟電子閱讀器的出現,跟電子書密切相關。若讀者是多年的蘋果用戶使用者(無論是 iPhone 或 iPad),應該都知道一個叫做 iBooks(2018 年改名為 Apple Books)的 APP,我查了一下維基百科,發現它在 2010 年 iOS4,也就是 iPhone 4 的年代就已經存在了。而亞馬遜的 Kindle 則在更早的 2007 年發表了第一代的 Kindle 電子閱讀器。然而蘋果公司則從未聽到要研發電子閱讀的任何消息 ⋯⋯

紙本書及電子書的優缺點,我想網路上很多人都曾討論過這個議題,我自己的感想也是大同小異,如果不介意的話就繼續讀下去吧 😆

就先從優點開始講吧

偏好紙本書的人,通常不外乎就是因為:

  • 喜歡翻書的感覺、紙的觸感
  • 新書的紙好香,油墨味好香
  • 看實體書好有氣質(虛榮感氾濫 )
  • 整面書櫃一眼望去,收藏慾的大滿足
  • 特典彩繪書衣、或是作者親筆簽名(腦粉大爆發)
  • 來回查閱方便直覺
  • 可以用筆做筆記
  • 可以借給別人
  • 可以二手脫售

再來說說電子書的優點

  • 輕薄輕量,一間圖書館隨手帶著走(古人無法想像飛機在天上飛的概念)
  • 通常價格比實體書便宜,而且購買方便(就跟蒐集 Steam 遊戲一樣簡單。什麼?我剛說蒐集嗎?)
  • 若是看很多外語書,那購買也更方便
  • 容易利用零碎時間閱讀(包括雨天濕漉漉的通勤日)
  • 可以全域搜尋、詞彙翻譯、生詞查詢、維基百科條目查詢
  • 可以在不同裝置閱讀,而且同步閱讀進度
  • 重量很重,也佔空間(搬過家的人應該都知道書有多重)
  • 紙張需要保養及維護(溫度與濕度的控制很重要,還要防蟲害)
  • 地震的時候很可怕(聽說日本 311 大地震時,很多人被倒下的書櫃壓到)
  • 二手書不一定好賣
  • 藍光傷眼的問題(若使用手機或平板等裝置)
  • 推播訊息,其他的 APP 會使人容易分心(若使用手機或平板等裝置)
  • 來回查閱比較困難,而且不直覺
  • 少了翻書的感覺、書本的味道
  • 人家不知道你/妳是在滑手機,還是在看文學巨著(又是莫名的虛榮心)
  • 不能墊泡麵(誤)

從上面我整理出的優缺點看來,紙本書與電子書都沒有壓倒性的優勢,可以說是各有各的好處。

有些人因為電子書沒有看書的感覺、沒有紙的觸感,所以不想看電子書。有位朋友曾經說:「我買書來收藏,才不要花錢買一個檔案。」收藏家的浪漫是可以理解的。但試想一下,現在的作家應該大部分都是用電腦寫書了吧,所以在出版社尚未發行之前,基本上書的本體就是一個電子檔案,作家在向出版社交稿的時候,也是用電子檔的形式吧?

仔細想想,人類文明裡,書籍的載體變動了很多次,古埃及的紙莎草、石碑、美索不達米亞的泥板、古中國的竹簡、中古歐洲的羊皮紙 ⋯⋯ 等等。而以紙本為載體的形式也存在了很長的時間,人們自然也將紙張跟書本聯想在一起,對於電子書這件事當然會有很多的不習慣。然而再想想,現在電子郵件幾乎取代了以往的紙本書信,電子書要全面取代紙本書也並非不可能,雖然也沒有期待它發生,我還是很喜歡紙本書的。對很多人來說,紙本書或許也象徵著文明的基礎、文化的底蘊,如果都電子化或許就沒意思了。

只能說,兩者的優點在於使用情境的不同,才能夠發揮所長。工具書為了查找方便,所以使用紙本書;娛樂性質的小說,買電子書存放在手機或閱讀器裡,省去看完很難處理的困擾;收藏一整套火影忍者的漫畫,光是擺在架上就賞心悅目 ⋯⋯。當然,如果收藏一整套電子版的火影忍者,隨時隨地都可以翻閱享受,那又是另一種收藏的浪漫。

有時候,書是工具、是資訊的載體;但在某些情況下,書本身更是擁有者的收藏。

於是我可以這樣子歸納:

若一本書記載的資訊遠大於書本身,就買電子書。

若一本書的收藏意義重大(內容固然也很棒),就買紙本書。

牙醫與臭豆腐

晚餐後有預約看牙醫。

雖然是預約看牙醫,但實際上牙齒並沒有什麼不適,只是做定期的洗牙維護罷了。在吃晚餐之前,突然想到要看牙醫這件事。不是我忘記今天要看牙醫,而是突然想到:「看牙醫之前,吃東西也沒有禁忌這件事?」不過聽到禁忌不要誤會了,不是指迷信那回事。我在思考的問題是,打個比方好了:如果看牙醫之前去吃臭豆腐的話,牙醫會不會在你嘴巴張開的那一刻,皺起眉頭呢?

我以前也想過這些事,各科的醫生有各種辛苦。皮膚科醫生可能每天看著滿臉痘子的病人、臭腳伸得高高有長雞眼的病人。外科醫生時常看到病人身體裡的各種內臟、還有滿滿的紅色。牙醫每天要聞著從病人的口中飄出來的各種味道,一邊辛苦地工作。每次這樣想像著,都會不禁感嘆:「啊,這種工作我還真做不來,跟生命有關,尤其跟人命有關的工作,我真的沒辦法。」倒不是因為看到血會昏倒這麼簡單的理由。

回到臭豆腐的話題,如果換成吃麻辣鍋,不知道牙醫師會是什麼心情?如果工作忙到餓著肚子還沒吃飯,卻又從病人口中聞到各種美食的味道,不是很令人沮喪嗎?不過畢竟飯還是要吃的,最後我吃了清淡的飯菜、配上了味噌湯,或許會有味噌湯的味道。不過不要吃臭豆腐的話,應該在可接受的範圍吧?我暗自認為牙醫師會這麼想。各位去看牙醫之前,會特別做什麼事嗎?例如:吃個涼糖、刷個牙、用漱口水漱個口,還是不會特別做什麼?

[CSS] Tailwind 主題色的切換

必備知識:CSSHTMLJSReact

Tailwind 提供了深色模式(Dark Mode),可以自行設定深色模式時的樣式,只要在 CSS class 前面加上 dark: 關鍵字:

<div class="text-black dark:text-white">
{/* ... */}
</div>

假如我們要做「主題色切換」的功能,dark: 這個用法也許可行,但也只限於兩個主題色的切換,另一個缺點就是到處都要加上 dark: 的切換樣式,這聽起來不是一個好的做法。

想跳過本文廢話、直接看最終實作結果的同學,請走傳送門

在 Tailwind config 的自定義主題色盤

Section titled “在 Tailwind config 的自定義主題色盤”

在 Tailwind 的 config 當中,其中一個客制化的選項,就是可以自定義顔色的 class,例如:

module.exports = {
theme: {
colors: {
primary: "#0d6efd",
secondary: "#6c757d",
danger: "#dc3545",
warning: "#ffc107",
},
},
};

PS. 以上的名稱及色碼取自 Bootstrap 的主題色盤

若我們有兩個主題色,config 就必須寫成這樣:

module.exports = {
theme: {
colors: {
"theme-1-primary": "#0d6efd",
"theme-1-secondary": "#6c757d",
"theme-1-danger": "#dc3545",
"theme-1-warning": "#ffc107",
"theme-2-primary": "#0d6efd",
"theme-2-secondary": "#6c757d",
"theme-2-danger": "#dc3545",
"theme-2-warning": "#ffc107",
},
},
};

如此一來,就要在 HTML 上去抽換不同主題的 class:

function Button() {
const buttonTheme = isTheme1 ? 'bg-theme-1-primary' : 'bg-theme-2-primary';
return <button className={buttonTheme}>;
}

到處都要有這樣的判斷,看來也不是明智的做法。此外,若我們有四、五個主題要切換,就不是三元運算可以簡單解決的事。

CSS 提供了變數的功能,可分爲區域變數與全域變數。

定義 CSS 區域變數,下面這個 --main-bg-color 只能在這個 scope({} 刮號)裡面使用:

element {
--main-bg-color: brown;
}

比較常見使用 CSS 變數的方式,是使用 CSS 全域變數,建立一個稱作 :root 的 pseudo-class,裡面定義的變數在整個 HTML document 底下都可以使用(其實就是全域變數了):

:root {
--main-bg-color: brown;
}

使用 CSS 變數時,用 var() 這個函式,將要使用的變數作爲參數傳進去:

element {
background-color: var(--main-bg-color);
}

我們結合 Tailwind 及 CSS 變數的特性,就可以達到主題色盤抽換的目的,我們將方才 config 中寫死的色碼,用 CSS 變數取代:

module.exports = {
theme: {
colors: {
'color-one': 'var(--color-one)',
'color-two': 'var(--color-two)',
'color-three': 'var(--color-three)',
'color-four': 'var(--color-four)',
'color-five': 'var(--color-five)',
},
}
}

設定好 Tailwind config 之後,接著就是定義各主題色盤變數的時候了

const theme_ayanami = `
:root {
--color-one: #1d446c;
--color-two: #f1f1f1;
--color-three: #571a1a;
--color-four: #000000;
--color-five: #525252;
}
`;
const theme_ikari = `
:root {
--color-one: #3f6d4e;
--color-two: #8bd450;
--color-three: #1d1a2f;
--color-four: #965fd4;
--color-five: #734f9a;
}
`;

然後將變數包在 <style> 後,安插在 <head> 裡面:

function App() {
const [currentTheme, setCurrentTheme] = useState("ayanami");
const getThemeVariables = (_theme) => {
switch (_theme) {
case "ayanami":
return theme_ayanami;
break;
case "ikari":
return theme_ikari;
break;
}
};
useEffect(() => {
if (!document.getElementById("customThemeId")) {
const head = document.head;
const newStyleElement = document.createElement("style");
head.appendChild(newStyleElement);
newStyleElement.id = "customThemeId";
newStyleElement.innerHTML = getThemeVariables(currentTheme);
} else {
const styleElement = document.getElementById("customThemeId");
if (styleElement) {
// 更新 CSS 全域變數
styleElement.innerHTML = getThemeVariables(currentTheme);
}
}
}, [currentTheme]);
return (
<div>
<button onClick={() => setCurrentTheme("ayanami")}>凌波零</button>
<button onClick={() => setCurrentTheme("ikari")}>碇真嗣</button>
</div>
);
}

在這裡定義了兩個主題,分別是「零號機」與「初號機」的主題色,此後,就可以在 HTML 上使用了,例如:

<div class="bg-color-one"></div>

藉由抽換 <style> 的内容,將新切換的主題變數複寫進去,便可以主題切換了!

從瀏覽器的 Dev Tools 中,可以看到我們只抽換了 CSS 的全域變數,元件的部分都沒有異動

同場加映!「初音未來」跟「涼宮春日」的主題色!😘

若想看整個範例的結果,請前往這裡

範例原始碼,請參考這裡

我們利用 CSS 全域變數的特性,並搭配 Tailwind 的客制化顔色設定,實現了主題色切換的目的。優點非常顯著,我們在切換主題的時候,不用動用任何一行原本的 HTML、不用改動任何的 CSS class,也不用在元件裡面寫切換主題的邏輯,就可以快速達成主題切換的效果。

再來是 CSS 全域變數的部分,各主題的變數其實就是一個字串,上面的範例直接放在 JS 裡面,因此若遇到要更新主題顔色、或新增新主題色時,仍必須重新打包(build)一次前端的專案。若要避免重新打包,就要看 CI/CD 的架構策略,將這些字串移到別的地方,然而這不在我目前擅長的領域,也非本次討論的重點,就交由各位同學去發揮吧!

以上,Tailwind 主題切換分享到這裡,祝各位開發愉快 😎

初探多執行緒實作模式

本篇是略讀「Multithreaded JavaScript」這本書第六章:「多執行緒實作模式」(Multithreaded Patterns),整理的一些筆記

本章節介紹了一些多執行緒常見的實作模式,有以下:

  1. 執行緒池(Thread Pool)
  2. 互斥鎖(Mutex)
  3. 環形緩衝(Ring Buffers)
  4. 演員模型(Actor Model)
  • 多執行緒應用程式很常使用的一個實作方式
  • 執行緒池是一個集合,裡面含有同質性(homogeneous)的 worker 執行緒,每個 worker 都可用來處理重負載、複雜運算的工作
  • Node.js 的 libuv 函式庫提供了執行緒池的功能,預設為四個執行緒,可以處理底層 I/O 的一些操作
  • 概念很類似分散式系統
  • 分為兩部分討論:執行緒池的大小(Pool Size)與委派策略(Dispatch Strategies)
  • 一般而言不會動態改變執行緒池的大小
  • 通常在作業系統中,處理器核心跟執行緒並沒有直接的關聯
  • 當執行緒的數量遠超過處理器核心數量時,效能反而會下降
  • 以 Node.js 的 libuv 函式庫為例,裡面包含三個執行緒,分別是:主執行緒、worker 執行緒、垃圾回收執行緒(Garbage Collection Thread)
Node.js
// browser
cores = navigator.hardwareConcurrency;
cores = require("os").cpus().length;
  • 別忘了還有主執行緒,所以是 n + 1
  • 依據不同的目的來決定執行緒的數量
    • 對於挖礦而言,99.9% 的工作在於 worker 上所執行繁重的運算,幾乎沒有 I/O,主執行緒也沒有特別的事情,因此可以開與核心數量相同的 worker 執行緒
    • 對串流影音或轉檔而言,則有大量的 CPU 運算及 I/O 操作,因此必須預留兩個核心給這兩個程序,剩下再分配給 worker 執行緒
  • 如果不確定要如何分配,那麼留一個核心給主執行緒是一個安全的作法

我們將高成本、繁重的運算收集整理起來,然後分配個 worker 執行緒去執行這些任務,這裡介紹三個常見的委派策略:

  • 按照順序指派工作,指派到最後一位,下一次就回到第一位 worker 身上
  • 可以確保每位 worker 都有事做
  • 可能造成每位 worker 的負擔不均
  • HAProxy 稱此委派策略為 roundrobin
  • 就如字面的意思,隨機選取一個 worker 來處理工作
  • 可能造成每位 worker 的負擔不均
  • 將新的任務指派給負載最低的 worker
  • 當有最低負載的 worker 有兩個的時候,隨機選取一個指派
  • HAProxy 稱此委派策略為 leastconn
  • Mutex 全名為 mutually exclusive lock.
  • 互斥鎖是一個存取共享資料的控制機制
  • 此機制確保共享資料在同一時間,只允許一項任務執行
  • 互斥鎖在有人存取共享資料的時候上鎖,並在結束後解鎖
  • 在上鎖及釋放鎖定之間,稱之為「臨界區段」(Critical Section)

串流資料與環形緩衝(Ring Buffers)

Section titled “串流資料與環形緩衝(Ring Buffers)”
  • 環形緩衝是「先進先出」(first-in-first-out, aka FIFO)佇列的典型實作,利用一組「配對指標」指向當下的記憶體位址
  • 當佇列的指標在陣列末位,下一步會移動到陣列首位,形成環形結構的概念
  • 在北美餐廳中常被使用的點餐圓盤(order wheel)則是類比世界中,同樣的概念實踐
  • head 指標:指向下一個寫入佇列的位置
  • tail 指標:指向下一個讀取佇列的位置
  • 佇列長度:我們想要建立多大的佇列,一個陣列長度,head 指標、tail 指標會在上面移動

借用一下書中的示意圖:

ring buffer

  • 當寫入佇列時,head 指標移動的下一個位置
  • 當讀取佇列時,tail 指標移動的下一個位置
  • 當指標位在佇列末位,下一次就會移到首位
  • 因為是環形,所以沒有頭尾之分,因此指標在哪個位置並不重要
  • tail 指標最多只會跟 head 指標在同一個位置,不能超過 head 指標
  • 緩衝區滿載的時候,若要再寫入佇列,則有兩種策略:
    • 覆蓋最舊的佇列:相較於未處理的舊資料,新資料比較重要
    • 拋出異常,且不寫入佇列:資料順序性很重要的時候
  • 必須正確讀出最舊佇列
  • 儲存在環形緩衝裡的每個元素不會做移動,只有加入與移除,適合實作「先進先出」(first-in-first-out, aka FIFO)佇列
  • 「非環形緩衝」若要實作 FIFO,則必須在處理完一個任務之後,移動佇列上所有的元素
  • 「非環形緩衝」較適合實作「後進先出」(last-in-first-out, aka LIFO)佇列
  • JavaScript 的堆疊(stack)就是 LIFO 的實作
  • 動態改變佇列的大小,意味著必須重新賦予記憶體,會影響效能。若要實作動態佇列,鍊表(linked list)則較為適合
  • 演員模型是實踐同步運算的一種程式設計模式
  • 一個 actor 代表一個執行程式碼的容器
  • 在 Erlang 程式語言中,actor 是一級公民,但在 JavaScript 中也可以模擬其實作
  • 每個 actor 皆具有三種功能:執行運算、建立新的 actor、actor 之間的相互傳遞訊息
  • 每個 actor 擁有自己的訊息佇列(message queue),以 FIFO 佇列順序處理每個任務
  • 因為 actor 都無法操作共享記憶體(shared memory),因此避免了多執行緒容易發生的情況:race condition 及 deadlock
  • actor 是單執行緒,一次執行一件事
  • 使用 actor 的系統必須能夠接受延遲或順序不一致的現象
  • 每個 actor 可以擁有一個位址,例如:tcp://127.0.0.1:1234/3 代表著在 1234 port 中,第三個 actor

借用一下書中的示意圖:

actor model