1# Synchronous Task Development (TaskPool and Worker) 2 3 4Synchronous tasks are executed in order among multiple threads. Synchronization primitives, such as locks, are used by these tasks for coordination between each other. 5 6 7To implement synchronous tasks, you must consider the collaboration and synchronization between multiple threads and ensure the correctness of data and program execution. 8 9If synchronous tasks are independent of each other, you are advised to use **TaskPool**, which focuses on single independent tasks. For example, a series of imported static methods or methods implemented in singletons are independent. If synchronous tasks are associated with each other, use **Worker**, for example, methods implemented in class objects (not singleton class objects). 10 11 12## Using TaskPool to Process Independent Synchronous Tasks 13 14**TaskPool** is recommended for scheduling independent tasks. Typical independent tasks are those using static methods. If a unique handle or class object constructed using a singleton points to multiple tasks and these tasks can be used between different worker threads, you can also use **TaskPool**. 15 16> **NOTE** 17> 18> Due to memory isolation between different threads in the [Actor model](actor-model-development-samples.md), common singletons cannot be used across threads. This issue can be solved by exporting a singleton through the sendable module. 19 201. Define a concurrency function that internally calls the synchronous methods. 21 222. Create a [task](../reference/apis-arkts/js-apis-taskpool.md#task), call [execute()](../reference/apis-arkts/js-apis-taskpool.md#taskpoolexecute-1) to execute the task, and perform operations on the result returned by the task. 23 243. Perform concurrent operations. 25 26Simulate a singleton class that contains synchronous calls. 27 28 29```ts 30// Handle.ets code 31"use shared" 32 33@Sendable 34export default class Handle { 35 private static instance: Handle = new Handle(); 36 static getInstance(): Handle { 37 // Return a singleton object. 38 return Handle.instance; 39 } 40 41 static syncGet(): void { 42 // Synchronous getter. 43 } 44 45 static syncSet(num: number): number { 46 // Simulate synchronization step 1. 47 console.info("taskpool: this is 1st print!"); 48 // Simulate synchronization step 2. 49 console.info("taskpool: this is 2nd print!"); 50 return ++num; 51 } 52 53 static syncSet2(num: number): number { 54 // Simulate synchronization step 1. 55 console.info("taskpool: this is syncSet2 1st print!"); 56 // Simulate synchronization step 2. 57 console.info("taskpool: this is syncSet2 2nd print!"); 58 return ++num; 59 } 60} 61``` 62 63 64In the following example, **TaskPool** is used to call related synchronous methods. Specifically, define the concurrent function **func**, which is decorated using the [@Concurrent decorator](../arkts-utils/arkts-concurrent.md). Then define the **asyncGet** function, which is used to create a task, execute the task, and print the returned result. Finally, call the **asyncGet** function in the main thread and observe the execution process. 65 66 67```ts 68// Index.ets code 69import { taskpool} from '@kit.ArkTS'; 70import Handle from './Handle'; // Return a static handle. 71 72// Step 1: Define a concurrency function that internally calls the synchronous methods. 73@Concurrent 74async function func(num: number): Promise<number> { 75 // Call the synchronous wait implemented in a static class object. 76 // Call syncSet and use its result as an input parameter of syncSet2 to simulate the synchronous call logic. 77 let tmpNum: number = Handle.syncSet(num); 78 console.info("this is Child_Thread") 79 return Handle.syncSet2(tmpNum); 80} 81 82// Step 2: Create and execute a task. 83async function asyncGet(): Promise<void> { 84 // Create task and task2 and pass in the function func. 85 let task: taskpool.Task = new taskpool.Task(func, 1); 86 let task2: taskpool.Task = new taskpool.Task(func, 2); 87 // Execute task and task2 synchronously by using await. 88 let res: number = await taskpool.execute(task) as number; 89 let res2: number = await taskpool.execute(task2) as number; 90 // Print the task result. 91 console.info("taskpool: task res is: " + res); 92 console.info("taskpool: task res2 is: " + res2); 93} 94 95@Entry 96@Component 97struct Index { 98 @State message: string = 'Hello World'; 99 100 build() { 101 Row() { 102 Column() { 103 Text(this.message) 104 .fontSize(50) 105 .fontWeight(FontWeight.Bold) 106 .onClick(async () => { 107 // Step 3: Perform concurrent operations. 108 asyncGet(); 109 let num: number = Handle.syncSet(100); 110 console.info("this is Main_Thread!") 111 }) 112 } 113 .width('100%') 114 .height('100%') 115 } 116 } 117} 118``` 119 120 121## Using Worker to Process Associated Synchronous Tasks 122 123Use **Worker** when you want to schedule a series of synchronous tasks using the same handle or depending on the same class object. 124 1251. Create a **Worker** object in the main thread and receive messages from the worker thread. 126 127 ```ts 128 // Index.ets 129 import { worker } from '@kit.ArkTS'; 130 131 @Entry 132 @Component 133 struct Index { 134 @State message: string = 'Hello World'; 135 136 build() { 137 Row() { 138 Column() { 139 Text(this.message) 140 .fontSize(50) 141 .fontWeight(FontWeight.Bold) 142 .onClick(() => { 143 let w: worker.ThreadWorker = new worker.ThreadWorker('entry/ets/workers/MyWorker.ts'); 144 w.onmessage = (): void => { 145 // Receive the result of the worker thread. 146 } 147 w.onerror = (): void => { 148 // Receive error information of the worker thread. 149 } 150 // Send a Set message to the worker thread. 151 w.postMessage({'type': 0, 'data': 'data'}) 152 // Send a Get message to the worker thread. 153 w.postMessage({'type': 1}) 154 // ... 155 // Select a time to destroy the thread based on the actual situation. 156 w.terminate() 157 }) 158 } 159 .width('100%') 160 } 161 .height('100%') 162 } 163 } 164 ``` 165 166 1672. Bind the **Worker** object in the worker thread and process the synchronous task logic. 168 169 ```ts 170 // handle.ts code 171 export default class Handle { 172 syncGet() { 173 return; 174 } 175 176 syncSet(num: number) { 177 return; 178 } 179 } 180 ``` 181 182 ```ts 183 // MyWorker.ts code 184 import { worker, ThreadWorkerGlobalScope, MessageEvents } from '@kit.ArkTS'; 185 import Handle from './handle' // Return a handle. 186 187 let workerPort : ThreadWorkerGlobalScope = worker.workerPort; 188 189 // Handle that cannot be transferred. All operations depend on this handle. 190 let handler: Handle = new Handle() 191 192 // onmessage() logic of the worker thread. 193 workerPort.onmessage = (e : MessageEvents): void => { 194 switch (e.data.type as number) { 195 case 0: 196 handler.syncSet(e.data.data); 197 workerPort.postMessage('success set'); 198 break; 199 case 1: 200 handler.syncGet(); 201 workerPort.postMessage('success get'); 202 break; 203 } 204 } 205 ``` 206