1e41f4b71Sopenharmony_ci# Reasonably Using Multithreaded Shared Memory
2e41f4b71Sopenharmony_ci## Introduction
3e41f4b71Sopenharmony_ciDuring application development, some time-consuming operations are executed in subthreads to prevent the main thread from being blocked. In this case, the subthreads need to access data in the main thread. ArkTS adopts the actor model that is based on message communication and therefore features memory isolation, which requires data serialization during cross-thread transmission. However, ArkTS supports shared memory through SharedArrayBuffer.
4e41f4b71Sopenharmony_ci
5e41f4b71Sopenharmony_ciIf the data volume is large and multiple threads are required for simultaneous operations, you are advised to use SharedArrayBuffer to reduce the extra overhead caused by data replication and serialization during cross-thread transmission. Example scenarios are audio/video decoding and playback, and simultaneous file read and write by multiple threads. In addition, you can use locks to prevent data disorder when multiple threads simultaneously operate the same memory. This topic describes SharedArrayBuffer and locks in details. For details about the usage and principles of multiple threads, see [Multithread Capability Scenarios](./multi_thread_capability.md).
6e41f4b71Sopenharmony_ci## Working Principles 
7e41f4b71Sopenharmony_ciA **SharedArrayBuffer** object is a raw binary data buffer with a fixed length. It can store any type of data including numbers and strings. It can be transferred between multiple threads. The objects before and after the transfer point to the same memory block, achieving memory sharing. If multiple operations are simultaneously performed by subthreads to modify data stored in a **SharedArrayBuffer** object, you must use atomic operations to ensure data synchronization. Atomic operations ensure that the current operation is complete before the next operation starts. The following uses an example to describe the necessity of atomic operations for purposes of synchronization. The full code can be obtained from [AtomicsUsage.ets](https://gitee.com/openharmony/applications_app_samples/blob/master/code/Performance/PerformanceLibrary/feature/memoryShared/src/main/ets/pages/AtomicsUsage.ets).
8e41f4b71Sopenharmony_ci### Non-atomic Operation
9e41f4b71Sopenharmony_ci
10e41f4b71Sopenharmony_ci```javascript
11e41f4b71Sopenharmony_ci......
12e41f4b71Sopenharmony_ci// Non-atomic operation, which performs auto-increment operations for 10,000 times.
13e41f4b71Sopenharmony_ci@Concurrent
14e41f4b71Sopenharmony_cifunction normalProcess(int32Array: Int32Array) {
15e41f4b71Sopenharmony_ci  for (let i = 0; i < 10000; i++) {
16e41f4b71Sopenharmony_ci    int32Array[0]++;
17e41f4b71Sopenharmony_ci  }
18e41f4b71Sopenharmony_ci}
19e41f4b71Sopenharmony_ci// Atomic operation, which performs auto-increment operations for 10,000 times.
20e41f4b71Sopenharmony_ci@Concurrent
21e41f4b71Sopenharmony_cifunction atomicsProcess(int32Array: Int32Array) {
22e41f4b71Sopenharmony_ci  for (let i = 0; i < 10000; i++) {
23e41f4b71Sopenharmony_ci    Atomics.add(int32Array, 0, 1);
24e41f4b71Sopenharmony_ci  }
25e41f4b71Sopenharmony_ci}
26e41f4b71Sopenharmony_ci......
27e41f4b71Sopenharmony_ci@State result: string = "Calculation result:";
28e41f4b71Sopenharmony_ciprivate taskNum: number = 2;
29e41f4b71Sopenharmony_ciprivate scroller: Scroller = new Scroller();
30e41f4b71Sopenharmony_ci......
31e41f4b71Sopenharmony_ciButton ("Non-atomic Operation")
32e41f4b71Sopenharmony_ci  .width("80%")
33e41f4b71Sopenharmony_ci  .fontSize(30)
34e41f4b71Sopenharmony_ci  .fontWeight(FontWeight.Bold)
35e41f4b71Sopenharmony_ci  .margin({ top: 30 })
36e41f4b71Sopenharmony_ci  .onClick(async () => {
37e41f4b71Sopenharmony_ci     this.sharedArrayBufferUsage(false);
38e41f4b71Sopenharmony_ci  })
39e41f4b71Sopenharmony_ciScroll(this.scroller) {
40e41f4b71Sopenharmony_ci  Column() {
41e41f4b71Sopenharmony_ci    Text(this.result)
42e41f4b71Sopenharmony_ci      .width("80%")
43e41f4b71Sopenharmony_ci      .fontSize(30)
44e41f4b71Sopenharmony_ci      .fontWeight(FontWeight.Bold)
45e41f4b71Sopenharmony_ci      .fontColor(Color.Blue)
46e41f4b71Sopenharmony_ci  }
47e41f4b71Sopenharmony_ci}.height("60%")
48e41f4b71Sopenharmony_ci.margin({ top: 30 })
49e41f4b71Sopenharmony_ci......
50e41f4b71Sopenharmony_ci// Determine whether to use atomic operations based on the passed-in value of isAtomics.
51e41f4b71Sopenharmony_cisharedArrayBufferUsage(isAtomics: boolean) {
52e41f4b71Sopenharmony_ci  // Create a 4-byte SharedArrayBuffer object.
53e41f4b71Sopenharmony_ci  let sab: SharedArrayBuffer = new SharedArrayBuffer(4);
54e41f4b71Sopenharmony_ci  // SharedArrayBuffer is a raw binary data buffer and cannot be directly used. Therefore, SharedArrayBuffer is converted to the Int32Array type for subsequent operations.
55e41f4b71Sopenharmony_ci  let int32Array: Int32Array = new Int32Array(sab);
56e41f4b71Sopenharmony_ci  int32Array[0] = 0;
57e41f4b71Sopenharmony_ci  let taskGroup: taskpool.TaskGroup = new taskpool.TaskGroup();
58e41f4b71Sopenharmony_ci  // Create a Task object and put it in the task group for execution.
59e41f4b71Sopenharmony_ci  for (let i = 0; i < this.taskNum; i++) {
60e41f4b71Sopenharmony_ci    if (isAtomics) {
61e41f4b71Sopenharmony_ci      taskGroup.addTask(new taskpool.Task(atomicsProcess, int32Array));
62e41f4b71Sopenharmony_ci    } else {
63e41f4b71Sopenharmony_ci      taskGroup.addTask(new taskpool.Task(normalProcess, int32Array));
64e41f4b71Sopenharmony_ci    }
65e41f4b71Sopenharmony_ci  }
66e41f4b71Sopenharmony_ci  taskpool.execute(taskGroup).then(() => {
67e41f4b71Sopenharmony_ci    // Print the result on the <Text> component.
68e41f4b71Sopenharmony_ci    this.result = this.result + "\n" + int32Array;
69e41f4b71Sopenharmony_ci    // If the mouse pointer is not at the bottom of the screen, scroll to the bottom.
70e41f4b71Sopenharmony_ci    if (!this.scroller.isAtEnd()) {
71e41f4b71Sopenharmony_ci      this.scroller.scrollEdge(Edge.Bottom);
72e41f4b71Sopenharmony_ci    }
73e41f4b71Sopenharmony_ci  }).catch((e: BusinessError) => {
74e41f4b71Sopenharmony_ci    logger.error(e.message);
75e41f4b71Sopenharmony_ci  })
76e41f4b71Sopenharmony_ci}
77e41f4b71Sopenharmony_ci
78e41f4b71Sopenharmony_ci```
79e41f4b71Sopenharmony_ciIn this code, two tasks are created, each of which is used to perform 10,000 auto-increment operations on the **SharedArrayBuffer** object. The expected result is 20000. The calculation result may not be 20000, and the result may be different each time you touch the **Non-atomic Operation** button. This is because SharedArrayBuffer is shared memory. When multiple threads perform auto-increment at the same time, the same memory block is operated. The auto-increment operation, however, is not an atomic operation and can be divided into the following three steps:
80e41f4b71Sopenharmony_ci- Step 1: Obtains the value from the memory.
81e41f4b71Sopenharmony_ci- Step 2: Increases the obtained value by 1.
82e41f4b71Sopenharmony_ci- Step 3: Writes the result to the memory.
83e41f4b71Sopenharmony_ci
84e41f4b71Sopenharmony_ciWhen multiple threads operate the memory at the same time, the following situation occurs: Thread A obtains the value 1000 from the memory and increases the value by 1 (the result is now 1001). Before thread A writes the result to the memory, thread B obtains the value, which is still 1000. Thread A writes 1001 to the memory. Thread B increases the value by 1 (the result is now 1001) and writes the result 1001 to the memory. As a result, the operation of increasing the value by 1 is performed twice, but the result is 1001 instead of 1002. The result does not meet the expectation.
85e41f4b71Sopenharmony_ci### Atomic Operations
86e41f4b71Sopenharmony_ciThe code below uses the atomic operation **Atomics.add()** to perform the auto-increment operation.
87e41f4b71Sopenharmony_ci
88e41f4b71Sopenharmony_ci```javascript
89e41f4b71Sopenharmony_ci......
90e41f4b71Sopenharmony_ciButton("Atomic Operation")
91e41f4b71Sopenharmony_ci  .width("80%")
92e41f4b71Sopenharmony_ci  .fontSize(30)
93e41f4b71Sopenharmony_ci  .fontWeight(FontWeight.Bold)
94e41f4b71Sopenharmony_ci  .margin({ top: 30 })
95e41f4b71Sopenharmony_ci  .onClick(async () => {
96e41f4b71Sopenharmony_ci    this.sharedArrayBufferUsage(true);
97e41f4b71Sopenharmony_ci  })
98e41f4b71Sopenharmony_ci......
99e41f4b71Sopenharmony_ci
100e41f4b71Sopenharmony_ci```
101e41f4b71Sopenharmony_ciNo matter how many times you touch the **Atomic Operation** button, the result is always 20,000. This is because the atomic operation is one or a series of operations that cannot be interrupted. It ensures the operation of thread A is not interrupted by thread B, and thread B always obtains the new value written by thread A to the memory. Therefore, when using SharedArrayBuffer, use atomic operations to ensure data synchronization and avoid data disorder.
102e41f4b71Sopenharmony_ci## Example
103e41f4b71Sopenharmony_ciIn the case of complex logic, it cannot be ensured the entire thread carries out an atomic operation. Locks are introduced to address this scenario.
104e41f4b71Sopenharmony_ci### Implementation of Locks
105e41f4b71Sopenharmony_ciConcurrent programming focuses on solving the problems of task division, synchronization, and mutual exclusion between threads, and locks are the important way to implement mutual exclusion. The code snippet below implements the lock **NonReentrantLock** by using Atomics and SharedArrayBuffer.
106e41f4b71Sopenharmony_ci
107e41f4b71Sopenharmony_ciThe **constructor()** method initializes the lock by passing in a **SharedArrayBuffer** object. Multiple threads operate the same shared memory and use a flag bit to control the lock status.
108e41f4b71Sopenharmony_ci
109e41f4b71Sopenharmony_ci```javascript
110e41f4b71Sopenharmony_ciconst UNLOCKED = 0;
111e41f4b71Sopenharmony_ciconst LOCKED_SINGLE = 1;
112e41f4b71Sopenharmony_ciconst LOCKED_MULTI = 2;
113e41f4b71Sopenharmony_ciexport class NonReentrantLock {
114e41f4b71Sopenharmony_ci  flag: Int32Array;
115e41f4b71Sopenharmony_ci  constructor(sab: SharedArrayBuffer) { // Pass in a 4-byte SharedArrayBuffer.
116e41f4b71Sopenharmony_ci    this.flag= new Int32Array(sab); // The view is a one-bit int array (1 = 4 bytes * 8 / 32 bits).
117e41f4b71Sopenharmony_ci  }
118e41f4b71Sopenharmony_ci   
119e41f4b71Sopenharmony_ci  lock(): void {...}
120e41f4b71Sopenharmony_ci  tryLock(): boolean {...}
121e41f4b71Sopenharmony_ci  unlock(): void {...}
122e41f4b71Sopenharmony_ci}
123e41f4b71Sopenharmony_ci
124e41f4b71Sopenharmony_ci```
125e41f4b71Sopenharmony_ciThe **lock()** method is used to obtain a lock. A thread that fails to obtain the lock is blocked.
126e41f4b71Sopenharmony_ci
127e41f4b71Sopenharmony_ci```javascript
128e41f4b71Sopenharmony_cilock(): void {
129e41f4b71Sopenharmony_ci  const flag= this.flag;
130e41f4b71Sopenharmony_ci  let c = UNLOCKED;
131e41f4b71Sopenharmony_ci  // If the current value is UNLOCKED at the index 0 of the flag array, the value is changed to LOCKED_SINGLE. Otherwise, the code enters the do-while loop and the thread is blocked.
132e41f4b71Sopenharmony_ci  if ((c = Atomics.compareExchange(flag, 0, UNLOCKED, LOCKED_SINGLE)) !== UNLOCKED) {
133e41f4b71Sopenharmony_ci    do {
134e41f4b71Sopenharmony_ci      // A thread fails to obtain the lock. Its flag bit is changed to LOCKED_MULTI, and the thread is blocked.
135e41f4b71Sopenharmony_ci      if (c === LOCKED_MULTI || Atomics.compareExchange(flag, 0, LOCKED_SINGLE, LOCKED_MULTI) !== UNLOCKED) {
136e41f4b71Sopenharmony_ci        Atomics.wait(flag, 0, LOCKED_MULTI);
137e41f4b71Sopenharmony_ci      }
138e41f4b71Sopenharmony_ci    // If the thread is woken up and fails to obtain the lock again, the thread returns to the loop and is blocked again.
139e41f4b71Sopenharmony_ci    } while ((c = Atomics.compareExchange(flag, 0, UNLOCKED, LOCKED_MULTI)) !== UNLOCKED);
140e41f4b71Sopenharmony_ci  }
141e41f4b71Sopenharmony_ci}
142e41f4b71Sopenharmony_ci
143e41f4b71Sopenharmony_ci```
144e41f4b71Sopenharmony_ciThe **tryLock()** method is used to try to obtain the lock. If the lock is obtained, **true** is returned. If the lock fails to be obtained, **false** is returned, but the thread is not blocked.
145e41f4b71Sopenharmony_ci
146e41f4b71Sopenharmony_ci```javascript
147e41f4b71Sopenharmony_citryLock(): boolean {
148e41f4b71Sopenharmony_ci  const flag= this.flag;
149e41f4b71Sopenharmony_ci  return Atomics.compareExchange(flag, 0, UNLOCKED, LOCKED_SINGLE) === UNLOCKED;
150e41f4b71Sopenharmony_ci}
151e41f4b71Sopenharmony_ci
152e41f4b71Sopenharmony_ci```
153e41f4b71Sopenharmony_ciThe **unlock()** method is used to release the lock.
154e41f4b71Sopenharmony_ci
155e41f4b71Sopenharmony_ci```javascript
156e41f4b71Sopenharmony_ciunlock(): void {
157e41f4b71Sopenharmony_ci  // Local flag to ensure that only the thread that has the lock can release the lock.
158e41f4b71Sopenharmony_ci  const flag= this.flag;
159e41f4b71Sopenharmony_ci  let v0 = Atomics.sub(flag, 0, 1);
160e41f4b71Sopenharmony_ci  if (v0 !== LOCKED_SINGLE) {
161e41f4b71Sopenharmony_ci    Atomics.store(flag, 0, UNLOCKED);
162e41f4b71Sopenharmony_ci    // Wake up only one thread waiting at the index 0 of the array, check the while condition in the lock() method, and try to obtain the lock.
163e41f4b71Sopenharmony_ci    Atomics.notify(flag, 0, 1);
164e41f4b71Sopenharmony_ci  }
165e41f4b71Sopenharmony_ci}
166e41f4b71Sopenharmony_ci
167e41f4b71Sopenharmony_ci```
168e41f4b71Sopenharmony_ci### Usage of Locks
169e41f4b71Sopenharmony_ciIn the example scenario below where multiple threads write data to a file, improper manipulation on the shared memory makes the thread insecure. Consequently, garbled characters are displayed in the output file. The lock **NonReentrantLock** implemented above is used to solve the problem.
170e41f4b71Sopenharmony_ci
171e41f4b71Sopenharmony_ciThe main thread uses the **startWrite(useLock: boolean)** method to enable multiple threads to write data to the file and uses the **useLock** parameter to determine whether to use the lock.
172e41f4b71Sopenharmony_ci
173e41f4b71Sopenharmony_ci```javascript
174e41f4b71Sopenharmony_ci@Component
175e41f4b71Sopenharmony_ciexport struct LockUsage {
176e41f4b71Sopenharmony_ci  taskNum: number = 10; // Number of tasks. The actual number of concurrent threads depends on the device.
177e41f4b71Sopenharmony_ci  baseDir: string = getContext().filesDir + '/TextDir'; // Application sandbox path of the file.
178e41f4b71Sopenharmony_ci  sabInLock: SharedArrayBuffer = new SharedArrayBuffer(4); // Shared memory used by the main thread to initialize the lock flag bit of the subthread.
179e41f4b71Sopenharmony_ci  sabForLine: SharedArrayBuffer = new SharedArrayBuffer(4); // Shared memory used by the main thread to initialize the offset bit of the subthread.
180e41f4b71Sopenharmony_ci  @State result: string = "";
181e41f4b71Sopenharmony_ci  build() {
182e41f4b71Sopenharmony_ci    Row() {
183e41f4b71Sopenharmony_ci      Column() {
184e41f4b71Sopenharmony_ci        // The button indicates that the lock is not used.
185e41f4b71Sopenharmony_ci        Button($r('app.string.not_use_lock'))
186e41f4b71Sopenharmony_ci          .width("80%").fontSize(30)
187e41f4b71Sopenharmony_ci          .fontWeight(FontWeight.Bold)
188e41f4b71Sopenharmony_ci          .margin({ top: 30 })
189e41f4b71Sopenharmony_ci          .onClick(async () => {
190e41f4b71Sopenharmony_ci            this.startWrite(false);
191e41f4b71Sopenharmony_ci          })
192e41f4b71Sopenharmony_ci        // The button indicates that the lock is used.
193e41f4b71Sopenharmony_ci        Button($r('app.string.use_lock'))
194e41f4b71Sopenharmony_ci          .width("80%")
195e41f4b71Sopenharmony_ci          .fontSize(30)
196e41f4b71Sopenharmony_ci          .fontWeight(FontWeight.Bold)
197e41f4b71Sopenharmony_ci          .margin({ top: 30 }) 
198e41f4b71Sopenharmony_ci          .onClick(async () => {
199e41f4b71Sopenharmony_ci            this.startWrite(true);
200e41f4b71Sopenharmony_ci          })
201e41f4b71Sopenharmony_ci        // Running status description
202e41f4b71Sopenharmony_ci        Text(this.result)
203e41f4b71Sopenharmony_ci          .width("80%")
204e41f4b71Sopenharmony_ci          .fontSize(30)
205e41f4b71Sopenharmony_ci          .fontWeight(FontWeight.Bold)
206e41f4b71Sopenharmony_ci          .fontColor(Color.Blue)
207e41f4b71Sopenharmony_ci          .margin({ top: 30 }) 
208e41f4b71Sopenharmony_ci      }
209e41f4b71Sopenharmony_ci      .width('100%')
210e41f4b71Sopenharmony_ci    }
211e41f4b71Sopenharmony_ci    .height('100%')
212e41f4b71Sopenharmony_ci  }
213e41f4b71Sopenharmony_ci  startWrite(useLock: boolean): void {
214e41f4b71Sopenharmony_ci    // Specify the running status as "Starting writing data to the file".
215e41f4b71Sopenharmony_ci    this.result = getContext().resourceManager.getStringSync($r('app.string.write_file_start'));  
216e41f4b71Sopenharmony_ci    // Initialize the offset for writing.
217e41f4b71Sopenharmony_ci    let whichLineToWrite: Int32Array = new Int32Array(this.sabForLine);
218e41f4b71Sopenharmony_ci    Atomics.store(whichLineToWrite, 0, 0);
219e41f4b71Sopenharmony_ci    // Enable multiple threads to write the file based on the offset.
220e41f4b71Sopenharmony_ci    // Initialize the lock through sabInLock:SharedArrayBuffer of the main thread to ensure that multiple threads operate the same lock flag.
221e41f4b71Sopenharmony_ci    // Initialize the offset bit through sabForLine:SharedArrayBuffer of the main thread to ensure that multiple threads operate the same offset.
222e41f4b71Sopenharmony_ci    let taskPoolGroup: taskpool.TaskGroup = new taskpool.TaskGroup();
223e41f4b71Sopenharmony_ci    for (let i: number = 0; i < this.taskNum; i++) {
224e41f4b71Sopenharmony_ci      taskPoolGroup.addTask(new taskpool.Task(createWriteTask, this.baseDir, i, this.sabInLock, this.sabForLine, useLock));
225e41f4b71Sopenharmony_ci    }
226e41f4b71Sopenharmony_ci    taskpool.execute(taskPoolGroup).then(() => {
227e41f4b71Sopenharmony_ci      // Specify the running status as "Writing the file succeeded."
228e41f4b71Sopenharmony_ci      this.result = this.result = getContext().resourceManager.getStringSync($r('app.string.write_file_success'));  
229e41f4b71Sopenharmony_ci    }).catch(() => {
230e41f4b71Sopenharmony_ci      // Specify the running status as "Failed to write the file."
231e41f4b71Sopenharmony_ci      this.result = getContext().resourceManager.getStringSync($r('app.string.write_file_failed'));  
232e41f4b71Sopenharmony_ci    })
233e41f4b71Sopenharmony_ci  }
234e41f4b71Sopenharmony_ci}
235e41f4b71Sopenharmony_ci
236e41f4b71Sopenharmony_ci```
237e41f4b71Sopenharmony_ciThe subthread writes data to the file at the specified position based on the offset and specifies the position to write next time by automatically incrementing the offset.
238e41f4b71Sopenharmony_ci
239e41f4b71Sopenharmony_ci```javascript
240e41f4b71Sopenharmony_ci@Concurrent
241e41f4b71Sopenharmony_ciasync function createWriteTask(baseDir: string, writeText: number, sabInLock: SharedArrayBuffer, sabForLine: SharedArrayBuffer, useLock: boolean): Promise<void> {
242e41f4b71Sopenharmony_ci  class Option {// Interface method parameter class for writing data to a file.
243e41f4b71Sopenharmony_ci    offset: number = 0;
244e41f4b71Sopenharmony_ci    length: number = 0;
245e41f4b71Sopenharmony_ci    encoding: string = 'utf-8';
246e41f4b71Sopenharmony_ci    
247e41f4b71Sopenharmony_ci    constructor(offset: number, length: number) {
248e41f4b71Sopenharmony_ci      this.offset = offset;
249e41f4b71Sopenharmony_ci      this.length = length;
250e41f4b71Sopenharmony_ci    }
251e41f4b71Sopenharmony_ci  }
252e41f4b71Sopenharmony_ci  // Initialize the output file directory.
253e41f4b71Sopenharmony_ci  let filePath: string | undefined = undefined;
254e41f4b71Sopenharmony_ci  filePath = baseDir + useLock ? "/useLock.txt" : "/unusedLock.txt";
255e41f4b71Sopenharmony_ci  if (!fs.accessSync(baseDir)) {
256e41f4b71Sopenharmony_ci    fs.mkdirSync(baseDir);
257e41f4b71Sopenharmony_ci  }
258e41f4b71Sopenharmony_ci  // Use SharedArrayBuffer passed in by the main thread to initialize the lock.
259e41f4b71Sopenharmony_ci  let nrl: NonReentrantLock | undefined = undefined;
260e41f4b71Sopenharmony_ci  if (useLock) {
261e41f4b71Sopenharmony_ci    nrl = new NonReentrantLock(sabInLock);
262e41f4b71Sopenharmony_ci  }
263e41f4b71Sopenharmony_ci  // Use SharedArrayBuffer passed in by the main thread to initialize the offset for file writing.
264e41f4b71Sopenharmony_ci  let whichLineToWrite: Int32Array = new Int32Array(sabForLine);
265e41f4b71Sopenharmony_ci  let str: string = writeText + '\n';
266e41f4b71Sopenharmony_ci  for (let i: number = 0; i < 100; i++) {
267e41f4b71Sopenharmony_ci    // Obtain the lock.
268e41f4b71Sopenharmony_ci    if (useLock && nrl !== undefined) {
269e41f4b71Sopenharmony_ci      nrl.lock();
270e41f4b71Sopenharmony_ci    }
271e41f4b71Sopenharmony_ci    // Write data to the file.
272e41f4b71Sopenharmony_ci    let file: fs.File = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
273e41f4b71Sopenharmony_ci    try {
274e41f4b71Sopenharmony_ci      fs.writeSync(file.fd, str, new Option(whichLineToWrite[0], str.length));
275e41f4b71Sopenharmony_ci    } catch (err) {
276e41f4b71Sopenharmony_ci      logger.error(`errorCode : ${err.code},errMessage : ${err.message}`);
277e41f4b71Sopenharmony_ci    }
278e41f4b71Sopenharmony_ci    fs.closeSync(file);
279e41f4b71Sopenharmony_ci    // Modify the offset to specify the position for the next write.
280e41f4b71Sopenharmony_ci    whichLineToWrite[0] += str.length;
281e41f4b71Sopenharmony_ci    // Release the lock.
282e41f4b71Sopenharmony_ci    if (useLock && nrl !== undefined) {
283e41f4b71Sopenharmony_ci      nrl.unlock();
284e41f4b71Sopenharmony_ci    }
285e41f4b71Sopenharmony_ci  }
286e41f4b71Sopenharmony_ci}
287e41f4b71Sopenharmony_ci
288e41f4b71Sopenharmony_ci```
289e41f4b71Sopenharmony_ciView the written file in the application sandbox directory. The **unusedLock.txt** file contains less than 1000 lines with garbled characters, as shown in Figure 1.
290e41f4b71Sopenharmony_ci
291e41f4b71Sopenharmony_ci**Figure 1** File written without using the lock
292e41f4b71Sopenharmony_ci
293e41f4b71Sopenharmony_ci![unusedLock.txt](./figures/not_use_lock.png)
294e41f4b71Sopenharmony_ci
295e41f4b71Sopenharmony_ciThe **usedLock.txt** file contains 1000 lines without garbled characters, as shown in Figure 2.
296e41f4b71Sopenharmony_ci
297e41f4b71Sopenharmony_ci**Figure 2** File written using the lock
298e41f4b71Sopenharmony_ci
299e41f4b71Sopenharmony_ci![usedLock.txt](./figures/use_lock.png)
300e41f4b71Sopenharmony_ci
301e41f4b71Sopenharmony_ci## Summary
302e41f4b71Sopenharmony_ciArkTS supports inter-thread communication through shared memory, although it uses the actor model based on message communication. When SharedArrayBuffer is used to implement shared memory, atomic operations and locks can be used to solve the problem of synchronization and mutual exclusion between threads. Proper use of multithreaded shared memory can improve application performance while ensuring thread security.
303e41f4b71Sopenharmony_ci
304e41f4b71Sopenharmony_ci <!--no_check-->