# 4.8 > Provide two programming examples in which multithreading does not provide better performance than a single-threaded solution. > > 中文翻譯:提供兩個程式例子,在多執行序下不會比單執行序有更好的效率 ## 答案 1. 串行操作爲主的程序,例如:圖像進行處理的各個步驟之間通常是無法並行的 2. 大量需要同步等待資源的程式 # 4.10 > Which of the following components of program state are shared across threads in a multithreaded process? > > 中文翻譯:在多線程過程中,以下哪些程序狀態組件在線程之間共享? ## 答案 - [ ] a. Register values(寄存器/CPU) - [x] b. Heap memory(動態配置變數) - [x] c. Global variables(全域變數) - [ ] d. Stack memory(區域變數) ### 解釋 stack:區域變數 堆疊區段(stack segment)用於儲存函數的區域變數,以及各種函數呼叫時需要儲存的資訊(例如函數返回的記憶體位址還有呼叫者函數的狀態等),每一次的函數呼叫就會在堆疊區段建立一個 stack frame,儲存該次呼叫的所有變數與狀態,這樣一來同一個函數重複被呼叫時就會有不同的 stack frame,不會互相干擾,遞迴函數就是透過這樣的機制來執行的。 heap:動態配置變數 heap 區段的記憶體空間用於儲存動態配置的變數,例如 C 語言的 malloc 以及 C++ 的 new 所建立的變數都是儲存於此。 堆疊區段一般的狀況會從高記憶體位址往低記憶體位址成長,而 heap 剛好從對面以相反的方向成長。 ### example code ```c #include <stdio.h> #include <pthread.h> #include <stdlib.h> #define NUM_THREADS 5 // Heap過去共享資料型態 struct SharedData { int value; }; // Global variables pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // Pthread function void *thread_function(void *arg) { struct SharedData *data = (struct SharedData *)arg; for (int i = 0; i < 3; i++) { pthread_mutex_lock(&mutex); // Lock printf("Thread: Reading shared value = %d\n", data->value); data->value++; printf("Thread: Modifying shared value to %d\n", data->value); pthread_mutex_unlock(&mutex); // UnLock } pthread_exit(NULL); } int main() { pthread_t threads[NUM_THREADS]; // malloc 就是去heap memory 拿記憶體 struct SharedData *shared_data = (struct SharedData *)malloc(sizeof(struct SharedData)); shared_data->value = 0; for (int i = 0; i < NUM_THREADS; i++) { pthread_create(&threads[i], NULL, thread_function, (void *)shared_data); } for (int i = 0; i < NUM_THREADS; i++) { pthread_join(threads[i], NULL); } printf("Final value of shared variable: %d\n", shared_data->value); free(shared_data); return 0; } ``` The threads of a multithreaded process share heap memory and global variables. Each thread has its separate set of register values and a separate stack. # 4.16 >A system with two dual-core processors has four processors available for scheduling. A CPU-intensive application (e.g., a program that spends most of its time on computation, not I/O or memory accesses) is running on this system. All input is performed at program start-up, when a single file must be opened and read sequentially. Similarly, all output is performed just before the program terminates, when the program results must be written sequentially to a single file. Between startup and termination, the program is entirely CPU-bound (e.g., only executing instructions). Your task is to improve the performance of this application by multithreading it. Specifically, you must determine: > > 中文翻譯:一個擁有兩個雙核處理器的系統,有四個處理器可供排程使用。正在此系統上運行一個CPU密集型應用程序(例如,一個大部分時間用於計算而非I/O或內存訪問的程序)。所有輸入均在程序啟動時進行,需要打開並按順序讀取單個文件。同樣,所有輸出都在程序結束前進行,需要將程序結果按順序寫入單個文件。在啟動和終止之間,程序完全依賴於CPU(例如,僅執行指令)。您的任務是通過將其多線程化來改善此應用程序的性能。具體來說,您必須確定: ## P1 > How many threads will you create to handle input and output? Briefly explain. > > 中文翻譯: 您將創建多少個線程來處理輸入和輸出?簡要解釋一下 ## 答案 Since the file must be accessed sequentially, the work of reading and writing it cannot reasonably be parallelized, and only a single thread should be used for each of these tasks. ## P1 > How many threads will you create for the CPU-bound portion of the application? Briefly explain. ## 答案 4個 題目有說 **four processors** # 5.14 > Most scheduling algorithms maintain a run queue, which lists processes eligible to run on a processor. On multicore systems, there are two general options: (1) each processing core has its own run queue, or (2) a single run queue is shared by all processing cores. What are the advantages and disadvantages of each of these approaches? > > 中文翻譯:大多數調度算法都維護一個運行隊列,其中列出了可在處理器上運行的進程。在多核系統上,有兩個常見的選擇: (1)每個處理核心都有自己的運行隊列,或者 (2)所有處理核心共享一個單一的運行隊列。 這兩種方法各有什麼優缺點? ## 答案 1. 第一種的優點在,任務不會互相干擾,也不需要等待資源,但需要更多管理以及同步,以避免資源競爭 2. 第二種的優點在簡單方便有效率的使用系統資源,而且較好管理,但必須管控資源開銷,同步問題,可能會造成效率不佳 # 5.18 > The following processes are being scheduled using a preemptive, priority-based, round-robin scheduling algorithm. Each process is assigned a numerical priority, with a higher number indicating a higher relative priority. For processes with the same priority, a round-robin scheduler will be used with a time quantum of 10 units. If a process is preempted by a higher-priority process, the preempted process is placed at the end of the queue. (a) Show the scheduling order of the processes using a Gantt chart. (b) What is the turnaround time for each process? (c) What is the waiting time for each process? | Thread | Priority | Burst | Arrival | | --- | -------- | -------- | -------- | | P1 | 8 | 15 | 0 | | P2 | 3 | 20 | 0 | | P3 | 4 | 20 | 20 | | P4 | 4 | 20 | 25 | | P5 | 6 | 5 | 45 | | P6 | 6 | 15 | 55 | ```mermaid gantt title Process Scheduling dateFormat m axisFormat %M section Processes P1:undone, after 0m, 15m P2:undone, after 0m, 20m P3:undone, after 20m, 20m P4:undone, after 25m, 20m P5:undone, after 45m, 5m P6:undone, after 55m, 15m ``` ## 答案 ### A ```mermaid gantt title Process Scheduling dateFormat m axisFormat %H:%M section Scheduling P1:P1, 0, 15m P2-1:P2-1, after P1, 5m P3-1:P3-1, after P2-1, 5m section round-robin P3-2:P3-2, after P3-1, 10m P4-1:P4-1, after P3-2, 10m P5:P5, after P4-1, 5m P6:P6, after P5, 15m P4-2:P4-2, after P6, 10m P3-3:P3-3, after P4-2, 10m P2-2:P2-2, after P3-3, 10m ``` ### B | Process | Time Start | Time End | Total | | ------- | ---------- | -------- |:----- | | P1 | 0 | 15 | 15 | | P2 | 15 | 95 | 80 | | P3 | 20 | 85 | 65 | | P4 | 35 | 75 | 40 | | P5 | 45 | 50 | 5 | | P6 | 50 | 65 | 15 | ### C | Process | Time Start | Time End | Wait | | ------- | ---------- | -------- |:----- | | P1 | 0 | 15 | 0 | | P2 | 15 | 95 | 60 | | P3 | 20 | 85 | 45 | | P4 | 35 | 75 | 20 | | P5 | 45 | 50 | 0 | | P6 | 50 | 65 | 0 | # 5.22 > Consider a system running ten I/O-bound tasks and one CPU-bound task. Assume that the I/O-bound tasks issue an I/O operation once for every millisecond of CPU computing and that each I/O operation takes 10 milliseconds to complete. Also assume that the context-switching overhead is 0.1 millisecond and that all processes are long-running tasks. Describe the CPU utilization for a round-robin scheduler when: (a) The time quantum is 1 millisecond (b) The time quantum is 10 millisecond ## 答案 ### A $\frac{1.0ms}{1.0ms+0.1ms(context-switching)}=0.909%$ ### B $\frac{20ms}{(10ms*1.1)+10.1ms(context-switching)}=0.94$ # 5.25 > Explain the differences in how much the following scheduling algorithms discriminate in favor of short processes: (a) FCFS (b) RR (c) Multilevel feedback queues ## 答案 a 先來先服務(FCFS):這種排程算法不太偏向於**短進程**,因為它按照進程到達的順序進行調度。如果有長進程在短進程之前到達,那麼短進程需要等待長進程執行完畢才能被執行,這可能導致**短進程的等待時間變長**。 b 輪轉(RR):這種排程算法傾向於短進程,因為它使用固定的時間片來輪流執行進程。**當一個時間片用完時,如果進程還沒有執行完,它會被放回隊列的末尾**,而短進程通常可以在一個時間片內完成執行,因此有更多的機會被執行 c 多級反饋隊列:多級反饋隊列排程算法也傾向於短進程,因為它將進程按照優先級分成多個隊列,並且對於短進程使用較短的時間片,對於**長進程使用較長的時間片**。**這意味著短進程有更高的機會被執行**,因為它們會在較短的時間內完成,並且會經常被提升到較高優先級的隊列中。 # 6.7 > The pseudocode of Figure 6.15 illustrates the basic push() and pop() operations of an array-based stack. Assuming that this algorithm could be used in a concurrent environment, answer the following questions: (a) What data have a race condition? (b) How could the race condition be fixed? ![image](https://hackmd.io/_uploads/SJnAlrCZC.png) ## 答案 ### A push跟POP都會影響top數據會在過程中被修改,而導致is_empty有時候調用會有競爭 ### B 在所有修改操作之前加上互斥鎖(mutex),以避免對堆疊頂部(top)的競爭條件。 # 6.15 > Explain why implementing synchronization primitives by disabling interrupts is not appropriate in a single-processor system if the synchronization primitives are to be used in user-level programs. > 中文翻譯:解釋為什麼如果要在使用者層級程式中使用同步原語,則透過停用中斷來實現同步原語在單處理器系統中不合適。 ## 答案 如果要在用戶級程序中使用同步原语,通過禁用中斷來實現同步原语在單處理器系統中是不合適的。如果一個用戶級程序被賦予禁用中斷的能力,那麼它可以禁用計時器中斷並阻止上下文切換發生,從而允許它使用處理器而不讓其他進程執行。 # 6.18 The implementation of mutex locks provided in Section 6.5 suffers from busy waiting. Describe what changes would be necessary so that a process waiting to acquire a mutex lock would be blocked and placed into a waiting queue until the lock became available. ## 答案 ``` push(item, mutex) { wait(mutex); // Block and wait for mutex if (top < SIZE) { stack[top] = top++; signal(mutex); // Release mutex } else { signal(mutex); // Release mutex ERROR; } } pop(mutex) { wait(mutex); // Block and wait for mutex if (!is_empty()) { top--; int item = stack[top]; signal(mutex); // Release mutex return item; } else { signal(mutex); // Release mutex ERROR; } } is_empty(mutex) { wait(mutex); // Block and wait for mutex if (top == 0) { signal(mutex); // Release mutex return true; } else { signal(mutex); // Release mutex return false; } } ```