NanoPangBZ / win_serial_port_cpp_class

使用win api实现的windows串口类
0 stars 0 forks source link

Feature #5

Open NanoPangBZ opened 9 months ago

NanoPangBZ commented 9 months ago

双接收线程交替接收,增强io性能

codeautopilot[bot] commented 9 months ago

Potential solution

To enhance IO performance by allowing dual receive threads to alternate, we will modify the main.cpp to create two threads for receiving data and ensure they alternate using a synchronization mechanism. In SerialPort.cpp, we will implement two new methods for the receiving threads that will be executed alternately, controlled by an atomic flag to ensure thread safety.

Code

main.cpp

#include <iostream>
#include <windows.h>
#include "SerialPort.h"
#include <vector>
#include <thread>
#include <mutex>
#include <atomic>

std::atomic<bool> readFlag(true);

void receiveData1(SerialPort* serialPort) {
    while (true) {
        if (readFlag.load()) {
            serialPort->receiveMethod1();
            readFlag.store(false);
        }
        Sleep(100); // Sleep to simulate alternating, adjust as needed
    }
}

void receiveData2(SerialPort* serialPort) {
    while (true) {
        if (!readFlag.load()) {
            serialPort->receiveMethod2();
            readFlag.store(true);
        }
        Sleep(100); // Sleep to simulate alternating, adjust as needed
    }
}

int main() {
    // ... (rest of the main function code remains the same)

    // Create two threads for receiving data
    std::thread receiverThread1(receiveData1, serialPort);
    std::thread receiverThread2(receiveData2, serialPort);

    // ... (rest of the main function code remains the same)

    // Join threads before exiting
    receiverThread1.join();
    receiverThread2.join();

    // ... (rest of the main function code remains the same)
}

SerialPort/SerialPort.cpp

// ... (rest of the SerialPort.cpp code)

// Add a new atomic boolean to the class to control alternating reads
std::atomic<bool> _readFlag;

// New method for the first receive thread
void SerialPort::receiveMethod1() {
    // Perform read operations similar to the existing _recieveThreadImpl
    // ...
    // After successful read, flip the flag
    _readFlag.store(false);
}

// New method for the second receive thread
void SerialPort::receiveMethod2() {
    // Perform read operations similar to the existing _recieveThreadImpl
    // ...
    // After successful read, flip the flag
    _readFlag.store(true);
}

// ... (rest of the SerialPort.cpp code)

In the main.cpp, we use an atomic boolean readFlag to control which thread should receive data. Each thread checks this flag before attempting to receive data and flips the flag after a successful read. This ensures that the threads alternate.

In SerialPort.cpp, we implement receiveMethod1 and receiveMethod2 which are called by the respective threads. These methods should contain the logic for receiving data from the serial port, similar to the existing _receiveThreadImpl method.

The std::atomic<bool> type is used for the readFlag to ensure atomicity of the flag operations, which is crucial for thread safety when the flag is being accessed by multiple threads.

The Sleep(100) calls in the receiveData1 and receiveData2 functions are placeholders to simulate the alternating behavior and should be adjusted according to the actual timing requirements.

It's important to note that this is a high-level solution and the actual implementation may require additional error handling, synchronization mechanisms, and testing to ensure robustness and correct behavior.

Changes on main.cpp To address the task at hand, we need to modify `main.cpp` to instantiate the `SerialPort` object and create two threads that will call the new receiving methods in `SerialPort.cpp`. We also need to ensure proper thread management and synchronization. Here's a practical proposal to solve the task: 1. Include the necessary headers for threading and synchronization. 2. Define two functions that will be executed by the threads for receiving data. 3. Instantiate the `SerialPort` object. 4. Create two threads, each calling one of the receiving functions. 5. Implement synchronization mechanisms to ensure that the two threads can safely access shared resources. 6. Manage the threads to ensure they are properly joined or detached as needed. Here's an example of how the modified `main.cpp` might look: ```cpp #include #include #include "SerialPort.h" #include #include #include std::mutex serial_mutex; void receiveData1(SerialPort* serialPort) { while (true) { std::lock_guard guard(serial_mutex); // Call the first receiving method (to be implemented in SerialPort.cpp) serialPort->receiveMethod1(); Sleep(100); // Sleep to simulate alternating, adjust as needed } } void receiveData2(SerialPort* serialPort) { while (true) { std::lock_guard guard(serial_mutex); // Call the second receiving method (to be implemented in SerialPort.cpp) serialPort->receiveMethod2(); Sleep(100); // Sleep to simulate alternating, adjust as needed } } int main() { std::vector _list = SerialPort::getSerialPortList(); char* t = "HelloWorld!\r\n"; for (const auto& portInfo : _list) { std::cout << portInfo.portName << std::endl; } SerialPort* serialPort = new SerialPort; // Assuming the event handler is still relevant, otherwise remove serialPort->registerEventHandler(serial_evt_handler, NULL); serialPort->connect(29, 921600); // Create two threads for receiving data std::thread receiverThread1(receiveData1, serialPort); std::thread receiverThread2(receiveData2, serialPort); // Main thread continues to send data while (true) { serialPort->write((uint8_t*)t, strlen(t)); Sleep(1000); } // Join threads before exiting receiverThread1.join(); receiverThread2.join(); delete serialPort; return 0; } ``` In this example, `receiveData1` and `receiveData2` are placeholder functions that need to be implemented in `SerialPort.cpp`. The `std::mutex` named `serial_mutex` is used to ensure that only one thread can access the `SerialPort` object at a time, preventing race conditions. Please note that the `Sleep(100)` calls in the `receiveData1` and `receiveData2` functions are placeholders to simulate the alternating behavior. The actual implementation may require a more sophisticated mechanism for alternating between threads, depending on the specific requirements and behavior of the `SerialPort` class. The `while (true)` loop in the main function is used to continuously send data. This loop should be adjusted according to the actual application's requirements, including proper exit conditions and cleanup. Lastly, before the program exits, we ensure that both threads are joined using `receiverThread1.join()` and `receiverThread2.join()`. This is important to prevent any threads from being left running in the background when the main thread finishes execution. If the threads are intended to run indefinitely, you might consider detaching them instead, but this requires careful management of resources to avoid issues such as memory leaks.
Changes on SerialPort/SerialPort.cpp To address the user's request for enhancing IO performance by having dual receive threads that alternate, we need to modify the `SerialPort.cpp` file to implement a thread-safe mechanism for reading operations and create two methods for the receiving threads that will be executed alternately. Here's a practical proposal to solve the task: 1. **Thread-Safe Mechanism**: We already have mutexes `_receiveBufLock`, `_hSerialReadLock`, and `_hSerialWriteLock` in place to ensure thread safety for buffer access and serial port read/write operations. 2. **Dual Receive Threads**: We need to create two methods for the receiving threads. These methods will be similar to the existing `_recieveThreadImpl` method but designed to work in an alternating fashion. 3. **Alternating Execution**: To alternate between two threads, we can use a shared atomic boolean flag that indicates which thread should read from the serial port. Each thread will check this flag before attempting to read. Here's a code outline for the new methods: ```cpp #include // Add a new atomic boolean to the class to control alternating reads std::atomic _readFlag; // New method for the first receive thread void SerialPort::_receiveThreadImpl1() { while (true) { if (_readFlag.load()) { // Perform read operations similar to the existing _recieveThreadImpl // ... // After successful read, flip the flag _readFlag.store(false); } } } // New method for the second receive thread void SerialPort::_receiveThreadImpl2() { while (true) { if (!_readFlag.load()) { // Perform read operations similar to the existing _recieveThreadImpl // ... // After successful read, flip the flag _readFlag.store(true); } } } // Modify the _connect method to start two receive threads bool SerialPort::_connect() { // Existing connection code... // ... // Initialize the read flag _readFlag.store(true); // Start the two receive threads std::thread receiveThread1(&SerialPort::_receiveThreadImpl1, this); std::thread receiveThread2(&SerialPort::_receiveThreadImpl2, this); // Detach the threads so they run independently receiveThread1.detach(); receiveThread2.detach(); // ... } ``` 4. **Initialization**: Initialize the `_readFlag` in the `SerialPort` constructor to ensure it has a defined state before the threads start. 5. **Thread Management**: Ensure that the threads are properly managed during the destruction of the `SerialPort` object. This may involve signaling the threads to stop and joining them if they are not detached. 6. **Testing**: After implementing the changes, thorough testing is required to ensure that the alternating mechanism works correctly and that there are no race conditions or deadlocks. This proposal outlines the necessary changes to the `SerialPort.cpp` file to implement the feature requested by the user. It's important to note that the actual implementation may require additional error handling and synchronization mechanisms to ensure robustness.