DEV Community

SameX
SameX

Posted on

HarmonyOS Case Practice: Design and Implementation of a High-Concurrency Data Acquisition System

This article aims to deeply explore the technical details of the Huawei HarmonyOS Next system (up to API 12 as of now), and is summarized based on actual development practices.
It mainly serves as a vehicle for technical sharing and communication. Mistakes and omissions are inevitable. Colleagues are welcome to put forward valuable opinions and questions so that we can make progress together.
This article is original content, and any form of reprint must indicate the source and the original author.

1. System Architecture and Requirement Analysis

Background:
In the era of the Internet of Things and big data, many application scenarios require concurrent data acquisition from multiple sensors. Such systems usually face I/O-intensive tasks. An efficient data acquisition system should not only ensure the performance of high-concurrency processing but also handle the consistency and security of data to ensure that no data race problems occur in a multi-threaded environment.
Requirements:

  • The system needs to concurrently acquire data from multiple sensors simultaneously.
  • Use a multi-threaded model to handle I/O-intensive tasks to improve the system's response speed.
  • Ensure the security and consistency of data acquisition and avoid data races.
  • Use exception handling and retry mechanisms to deal with data transmission failures.
  • The system should have good scalability and be able to support the addition and management of more sensors. Functional Requirements:
  • Concurrent acquisition of sensor data.
  • Real-time transmission and status feedback of data.
  • Ensure data consistency and system stability. ### 2. Management and Execution of TaskPool Concurrent Tasks TaskPool Overview: In ArkTS, TaskPool provides an efficient multi-threaded concurrent task scheduling mechanism. Through TaskPool, developers can create concurrent tasks and have these tasks executed in background threads. The advantage of TaskPool is that it allows developers to focus on the execution logic of tasks without having to worry about thread management and lifecycle. Task Management and Scheduling: In multi-sensor data acquisition, the data acquisition tasks of sensors are I/O-intensive. Therefore, we can use TaskPool to execute the data acquisition tasks of each sensor in different threads, enhancing the concurrent processing capability of tasks. #### Example of TaskPool Data Acquisition Task:
import { taskpool } from '@kit.ArkTS';
// Simulate a concurrent task of collecting data from a sensor
@Concurrent
async function collectSensorData(sensorId: string): Promise<string> {
    // Simulate I/O operation: collecting data from the sensor
    console.log(`Collecting data from sensor ${sensorId}...`);
    await delay(1000); // Simulate delay
    return `Sensor ${sensorId} data`;
}
// Simulate the delay function
function delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
}
Enter fullscreen mode Exit fullscreen mode

3. Application of Sendable Data Transmission Mechanism in Concurrency

Sendable Overview:
In ArkTS, Sendable data is a safe data type that can be transmitted between concurrent instances. When we transmit data in a multi-threaded environment, we can use Sendable to ensure the safety and consistency of data transmission between different threads. Sendable data can be transmitted in two ways: by reference and by copy.
Data Transmission Design:
For the data collected from each sensor, we can use the Sendable data structure to safely transmit it to the main thread, avoiding concurrent conflicts during the data transfer process.

Example of Sendable Data Transmission:

import { taskpool } from '@kit.ArkTS';
// Define the Sendable data class
@Sendable
class SensorData {
    constructor(public sensorId: string, public data: string) {}
}
// Collect sensor data and transmit it to the main thread
@Concurrent
async function collectSensorData(sensorId: string): Promise<SensorData> {
    // Simulate I/O operation: collecting data
    const data = await delayAndCollect(sensorId);
    return new SensorData(sensorId, data);
}
// Simulate data collection in an I/O task
async function delayAndCollect(sensorId: string): Promise<string> {
    await delay(1000); // Simulate I/O delay
    return `Sensor ${sensorId} data`;
}
Enter fullscreen mode Exit fullscreen mode

4. Optimization of I/O-Intensive Tasks

Characteristics of I/O-Intensive Tasks:
I/O-intensive tasks mainly include operations such as file reading and writing, network requests, and database access. Their characteristic is that the amount of computation is small, but I/O operations consume a lot of time. To improve performance, we can use asynchronous tasks and asynchronous locks to manage resources and avoid resource competition between threads.
Application of Asynchronous Locks:
Asynchronous locks (AsyncLock) can be used to protect shared resources in concurrent tasks and prevent data race problems caused by different threads accessing or modifying the same data simultaneously. In data acquisition, we can use asynchronous locks to ensure that no conflicts occur when writing data.

Example of Using Asynchronous Locks:

import { ArkTSUtils } from '@kit.ArkTS';
// Define an asynchronous lock
const lock = new ArkTSUtils.locks.AsyncLock();
@Concurrent
async function writeDataWithLock(sensorData: SensorData): Promise<void> {
    await lock.lockAsync(() => {
        // Simulate the writing operation
        console.log(`Writing data of ${sensorData.sensorId}: ${sensorData.data}`);
    });
}
Enter fullscreen mode Exit fullscreen mode

5. Exception Handling and Retry Mechanism

In concurrent tasks, sensor data acquisition may fail due to reasons such as network and hardware. Therefore, we need to design an exception handling and retry mechanism to ensure that retries are performed or other solutions are provided when data acquisition fails.

Example of Exception Handling and Retry:

@Concurrent
async function collectSensorDataWithRetry(sensorId: string, retries = 3): Promise<SensorData | null> {
    for (let attempt = 1; attempt <= retries; attempt++) {
        try {
            const data = await delayAndCollect(sensorId);
            return new SensorData(sensorId, data);
        } catch (error) {
            console.error(`Failed to collect data of ${sensorId}, attempt ${attempt}...`);
            if (attempt === retries) {
                console.error(`Final collection failure: ${sensorId}`);
                return null; // Return null to indicate collection failure
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Through this retry mechanism, we can ensure that when sensor collection fails, the system will perform a certain number of retries and provide an appropriate handling strategy when the final failure occurs.

6. Comprehensive Code Implementation: Multi-Sensor Data Acquisition System

We use TaskPool to concurrently execute the data acquisition tasks of multiple sensors, use Sendable to transmit data, and ensure the consistency of data and the stability of the system at the same time. The following is the comprehensive example code:

@Entry
@Component
struct DataCollector {
    @State sensorDataList: Array<string> = []
    build() {
        Column() {
            Button('Start collecting data')
             .onClick(() => {
                    this.startDataCollection();
                })
            // Display the collected sensor data
            ForEach(this.sensorDataList, (data) => {
                Text(data)
            })
        }
    }
    startDataCollection() {
        const sensors = ['sensor1', 'sensor2', 'sensor3'];
        sensors.forEach(sensorId => {
            // Start concurrent tasks to collect data
            let task: taskpool.Task = new taskpool.Task(collectSensorDataWithRetry, sensorId);
            taskpool.execute(task).then((result: SensorData | null) => {
                if (result) {
                    this.sensorDataList.push(`Collected data: ${result.sensorId} - ${result.data}`);
                } else {
                    this.sensorDataList.push(`Failed to collect ${sensorId}`);
                }
            }).catch(error => {
                console.error("Task execution failed: " + error);
            });
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

7. Summary

In this article, we have constructed a high-concurrency data acquisition system, demonstrating how to manage concurrent tasks using TaskPool in ArkTS, ensuring the security of data transmission using Sendable data, and combining the asynchronous lock mechanism to prevent data race problems. We have also implemented an exception handling and retry mechanism to ensure that various failures can be dealt with during the data acquisition process.
Through this case, we can see the powerful concurrent processing ability of ArkTS and how to ensure data consistency and the security of transmission in high-concurrency scenarios. This design provides a reference for the implementation of multi-sensor data acquisition, Internet of Things, and big data acquisition systems.

Top comments (0)