1# Communication Between Threads 2 3## Introduction 4 5During application development, some time-consuming tasks are executed in subthreads to prevent the main thread from being blocked, delivering a better user experience. Generally, a subthread can independently complete its task. However, in most cases, data needs to be transferred from the main thread to the subthread, or the task execution result needs to be returned from the subthread to the main thread. Therefore, communication between the main thread and subthread is necessary. This topic describes several example scenarios to show how to implement data communication between the main thread and subthreads in OpenHarmony application development. 6 7## Independent Execution of a Task 8 9If a time-consuming task can be executed independently by a subthread, the subthread only needs to return the execution result to the main thread after the task is executed. You can perform the following operations to implement this scenario. 10 11First, import the TaskPool module. 12 13```typescript 14import { taskpool } from '@kit.ArkTS'; 15``` 16Then, implement the task that the subthread needs to perform. 17```typescript 18@Concurrent // Methods executed in the task must be decorated by @Concurrent. Otherwise, they cannot be called. 19export function loadPicture(count: number): IconItemSource[] { 20 let iconItemSourceList: IconItemSource[] = []; 21 // Traverse and add six IconItem data records. 22 for (let index = 0; index < count; index++) { 23 const numStart: number = index * 6; 24 // Six images are used cyclically. 25 iconItemSourceList.push(new IconItemSource($r('app.media.nearby'), `item${numStart + 1}`)); 26 iconItemSourceList.push(new IconItemSource($r('app.media.scan'), `item${numStart + 2}`)); 27 iconItemSourceList.push(new IconItemSource($r('app.media.shop'), `item${numStart + 3}`)); 28 iconItemSourceList.push(new IconItemSource($r('app.media.cards'), `item${numStart + 4}`)); 29 iconItemSourceList.push(new IconItemSource($r('app.media.album'), `item${numStart + 5}`)); 30 iconItemSourceList.push(new IconItemSource($r('app.media.applet'), `item${numStart + 6}`)); 31 } 32 return iconItemSourceList; 33} 34``` 35Finally, call **execute** in the **TaskPool** class to execute the task. 36```typescript 37...... 38// Create a task. 39let lodePictureTask: taskpool.Task = new taskpool.Task(loadPicture, 30); 40// Execute the task and return the result. 41taskpool.execute(lodePictureTask).then((res: IconItemSource[]) => { 42 // Execution result of the loadPicture API. 43 this.iconItemSourceList = res; 44}) 45...... 46``` 47 48## Simultaneous Execution of Multiple Tasks 49 50If multiple tasks are executed simultaneously, their execution time and result return time vary according to the task complexity. If the main thread requires the execution results of all tasks, you can use the following code snippet: 51```typescript 52...... 53let taskGroup: taskpool.TaskGroup = new taskpool.TaskGroup(); 54taskGroup.addTask(new taskpool.Task(loadPicture, 30)); 55taskGroup.addTask(new taskpool.Task(loadPicture, 20)); 56taskGroup.addTask(new taskpool.Task(loadPicture, 10)); 57taskpool.execute(taskGroup).then((ret: IconItemSource[][]) => { 58 for (let i = 0; i < ret.length; i++) { 59 for (let j = 0; j < ret[i].length; j++) { 60 this.iconItemSourceList.push(ret[i][j]); 61 } 62 } 63}) 64...... 65``` 66In this scenario, all the tasks to be executed are placed in a task group. After all the tasks in the task group are executed, the execution result of each task is placed in an array and returned to the main thread. The main thread can obtain all task execution results at a time. 67 68In addition, if a task needs to process a large amount of data (for example, a list contains 10,000 data records), it is time-consuming to process all the data in one task. In this case, you can split the data into multiple sublists, allocate one task for each sublist, and combine the results of all the tasks. This pattern reduces the processing time and improves user experience. 69 70## Communication with the Main Thread During Task Execution 71 72If a subthread needs to periodically notify the main thread of the task status and data changes, or needs to return a large amount of data by segment (for example, a large amount of data read from the database), you can perform the following operations: 73 74First, implement a method to receive messages sent by the task. 75```typescript 76function notice(data: number): void { 77 console.info("The subthread task has been executed. Total images loaded:", data) 78} 79``` 80Then, add **sendData()** to the task to enable the subthread to send messages to the main thread. 81```typescript 82// Use sendData to notify the main thread of information in real time. 83@Concurrent 84export function loadPictureSendData(count: number): IconItemSource[] { 85 let iconItemSourceList: IconItemSource[] = []; 86 // Traverse and add six IconItem data records. 87 for (let index = 0; index < count; index++) { 88 const numStart: number = index * 6; 89 // Six images are used cyclically. 90 iconItemSourceList.push(new IconItemSource($r('app.media.nearby'), `item${numStart + 1}`)); 91 iconItemSourceList.push(new IconItemSource($r('app.media.scan'), `item${numStart + 2}`)); 92 iconItemSourceList.push(new IconItemSource($r('app.media.shop'), `item${numStart + 3}`)); 93 iconItemSourceList.push(new IconItemSource($r('app.media.cards'), `item${numStart + 4}`)); 94 iconItemSourceList.push(new IconItemSource($r('app.media.album'), `item${numStart + 5}`)); 95 iconItemSourceList.push(new IconItemSource($r('app.media.applet'), `item${numStart + 6}`)); 96 taskpool.Task.sendData(iconItemSourceList.length); 97 } 98 return iconItemSourceList; 99} 100``` 101Finally, use **onReceiveData()** to enable the main thread to receive messages. 102```typescript 103...... 104let lodePictureTask: taskpool.Task = new taskpool.Task(loadPictureSendData, 30); 105// Use notice to receive messages sent by the task. 106lodePictureTask.onReceiveData(notice); 107taskpool.execute(lodePictureTask).then((res: IconItemSource[]) => { 108 this.iconItemSourceList = res; 109}) 110...... 111``` 112In this way, the main thread can receive the data sent by the task through **notice()**. 113 114## Instant Communication Between the Worker Thread and Main Thread 115 116In ArkTS, Worker provides a limited number of threads that exist for a longer time than TaskPool threads. Multiple tasks may be executed in one [Worker thread](https://docs.openharmony.cn/pages/v4.0/en/application-dev/arkts-utils/worker-introduction.md/), and the execution duration or returned result of each task may be different. The host thread needs to call different methods in the Worker thread according to the actual situation, and the Worker thread needs to return the result to the host thread in time. You can perform the following operations to implement this scenario. 117 118First, create a Worker thread to execute different tasks based on parameters. 119```typescript 120import { worker, MessageEvents, ThreadWorkerGlobalScope } from '@kit.ArkTS'; 121 122const workerPort: ThreadWorkerGlobalScope = worker.workerPort; 123// The Worker thread receives messages from the main thread and calls the corresponding method based on the data type. 124workerPort.onmessage = (e: MessageEvents): void => { 125 if (typeof e.data === "string") { 126 try { 127 // The method to call does not carry an input parameter. 128 let res: string = workerPort.callGlobalCallObjectMethod("picData", "setUp", 0) as string; 129 console.error("worker: ", res); 130 } catch (error) { 131 // Exception handling. 132 console.error("worker: error code is " + error.code + " error message is " + error.message); 133 } 134 } else if (e.data instanceof Array) { 135 // Return the first four data records. 136 workerPort.postMessage(e.data.slice(0, 4)); 137 } 138} 139``` 140Then, create a **Worker** object in the main thread. When the button is touched, **postMessage** is called to send a message to the Worker thread, and **onmessage** of the **Worker** class is used to receive data returned by the Worker thread. 141```typescript 142import { worker, MessageEvents } from '@kit.ArkTS'; 143...... 144@State iconItemSourceList: IconItemSource[] = []; 145// Create a Worker object. 146workerInstance: worker.ThreadWorker = new worker.ThreadWorker("entry/ets/pages/workers/Worker.ts"); 147aboutToAppear() { 148 // Initialize the Worker object. 149 this.initWorker(); 150 for (let index = 0; index < 20; index++) { 151 const numStart: number = index * 6; 152 // Six images are used cyclically. 153 this.iconItemSourceList.push(new IconItemSource($r('app.media.nearby'), `item${numStart + 1}`)); 154 this.iconItemSourceList.push(new IconItemSource($r('app.media.scan'), `item${numStart + 2}`)); 155 this.iconItemSourceList.push(new IconItemSource($r('app.media.shop'), `item${numStart + 3}`)); 156 this.iconItemSourceList.push(new IconItemSource($r('app.media.cards'), `item${numStart + 4}`)); 157 this.iconItemSourceList.push(new IconItemSource($r('app.media.album'), `item${numStart + 5}`)); 158 this.iconItemSourceList.push(new IconItemSource($r('app.media.applet'), `item${numStart + 6}`)); 159 } 160} 161initWorker(){ 162 // Use onmessage to receive data returned by the Worker thread. 163 this.workerInstance.onmessage = (e: MessageEvents): void => { 164 if (e.data instanceof Array) { 165 this.iconItemSourceList = e.data; 166 } 167 } 168} 169...... 170Button ('Change the number of images to five', { type: ButtonType.Normal, stateEffect: true }.) 171 .fontSize(14) 172 .borderRadius(8) 173 .backgroundColor('# 317aff') 174 .width(250) 175 .height(60) 176 .margin({ 177 top: 30 178 }) 179 .onClick(() => { 180 // Transfer data to the Worker thread. 181 this.workerInstance.postMessage(this.iconItemSourceList); 182 }) 183...... 184``` 185In the sample code, the Worker thread performs two different processing. When the input data is of the string type, it calls **callGlobalCallObjectMethod** to synchronously call the method in the main thread. When the input data is of the array type, it returns the first four data records of the array to the main thread. In this way, instant communication between the main thread and Worker thread can be implemented. 186 187## Worker Thread Synchronously Calls a Method of the Main Thread 188 189If the Worker thread needs to call the method that has been implemented in the main thread, you can perform the following operations: 190 191First, implement the method in the main thread, create a **Worker** object, and register the method on the **Worker** object. 192```typescript 193import { worker } from '@kit.ArkTS'; 194// Create a Worker object. 195const workerInstance: worker.ThreadWorker = new worker.ThreadWorker("entry/ets/pages/workers/Worker.ts"); 196 197class PicData { 198 public iconItemSourceList: IconItemSource[] = []; 199 200 public setUp(): string { 201 for (let index = 0; index < 20; index++) { 202 const numStart: number = index * 6; 203 // Six images are used cyclically. 204 this.iconItemSourceList.push(new IconItemSource($r('app.media.nearby'), `item${numStart + 1}`)); 205 this.iconItemSourceList.push(new IconItemSource($r('app.media.scan'), `item${numStart + 2}`)); 206 this.iconItemSourceList.push(new IconItemSource($r('app.media.shop'), `item${numStart + 3}`)); 207 this.iconItemSourceList.push(new IconItemSource($r('app.media.cards'), `item${numStart + 4}`)); 208 this.iconItemSourceList.push(new IconItemSource($r('app.media.album'), `item${numStart + 5}`)); 209 this.iconItemSourceList.push(new IconItemSource($r('app.media.applet'), `item${numStart + 6}`)); 210 } 211 return "setUpIconItemSourceList success!"; 212 } 213} 214 215let picData = new PicData(); 216// Register the method on the Worker object. 217workerInstance.registerGlobalCallObject("picData", picData); 218workerInstance.postMessage("run setUp in picData"); 219``` 220Then, use [callGlobalCallObjectMethod](../reference/apis-arkts/js-apis-worker.md#callglobalcallobjectmethod11) of the **Worker** object to call **setUp()** in the main thread. 221```typescript 222...... 223try { 224 // The method to call does not carry an input parameter. 225 let res: string = workerPort.callGlobalCallObjectMethod("picData", "setUp", 0) as string; 226 console.error("worker: ", res); 227} catch (error) { 228 // Exception handling. 229 console.error("worker: error code is " + error.code + " error message is " + error.message); 230} 231...... 232``` 233 234 235