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