Как обмениваться данными между TThread в DLL и основным потоком?

Я пишу DLL в C++Builder XE6, которая создает отдельный поток (производный от TThread) для получения данных JSON с сервера REST каждые X секунд (с использованием TIdHTTP) и анализа данных JSON.

Поток заполняет простую структуру (без динамически выделяемых данных) проанализированными данными JSON в методе Execute() потока:

typedef struct
{
    char MyString[40 + 1];
    double MyDouble;
    bool MyBool;
} TMyStruct;

Поток должен хранить структуру в виде списка, например std::vector:

#include <vector>

std::vector<TMyStruct> MyList;

Поток добавит TMyStruct в список:

TMyStruct Data;
...
MyList.push_back(Data);

Список будет охраняться TCriticalSection для предотвращения повреждения данных.

DLL экспортирует функцию для извлечения TMyStruct из MyList.

bool __declspec(dllexport) __stdcall GetMyStruct (int Index, TMyStruct* Data)
{
    ...
}

Единственное, не знаю куда деть MyList...

Если я сделаю MyList глобальную переменную, она будет находиться в памяти основного потока и GetMyStruct() сможет получить к ней прямой доступ. Как поток получает доступ к MyList?

Если я сделаю MyList членом производного от TThread класса, он будет расположен в памяти потока, и поток сможет получить к нему прямой доступ. Как GetMyStruct() получает доступ к MyList?

Каков наилучший/предпочтительный/распространенный способ хранения MyList и доступа к нему в другом потоке?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
23
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

If I make MyList a global variable, it is located in the main thread's memory and GetMyStruct() can access it directly. How does the thread access MyList?

Точно так же. Все потоки в процессе могут свободно обращаться к глобальным переменным внутри этого процесса. Например:

#include <vector>
#include <System.SyncObjs.hpp>

typedef struct
{
    char MyString[40 + 1];
    double MyDouble;
    bool MyBool;
} TMyStruct;

std::vector<TMyStruct> MyList;
TCriticalSection *Lock = NULL; // why not std::mutex instead?

class TMyThread : public TThread
{
    ...
};
TMyThread *Thread = NULL;

...

void __fastcall TMyThread::Execute()
{
    TMyStruct Data;
    ...
    Lock->Enter();
    try {
        MyList.push_back(Data);
    }
    __finally {
        Lock->Leave();
    }
    ...
}

...

void __declspec(dllexport) __stdcall StartThread ()
{
    Lock = new TCriticalSection;
    Thread = new TMyThread;
}

void __declspec(dllexport) __stdcall StopThread ()
{
    if (Thread) {
        Thread->Terminate();
        Thread->WaitFor();
        delete Thread;
        Thread = NULL;
    }
    if (Lock) {
        delete Lock;
        Lock = NULL;
    }
}

bool __declspec(dllexport) __stdcall GetMyStruct (int Index, TMyStruct* Data)
{
    if (!(Lock && Thread)) return false;

    Lock->Enter();
    try {
        *Data = MyList[Index];
    }
    __finally {
        Lock->Leave();
    }

    return true;
}

If I make MyList a member of the TThread-derived class, it is located in the thread's memory and the thread can access it directly. How does GetMyStruct() access MyList?

Доступ к нему через указатель на объект потока. Например:

#include <vector>
#include <System.SyncObjs.hpp>

typedef struct
{
    char MyString[40 + 1];
    double MyDouble;
    bool MyBool;
} TMyStruct;

class TMyThread : public TThread
{
protected:
    void __fastcall Execute();
public:
    __fastcall TMyThread();
    __fastcall ~TMyThread();

    std::vector<TMyStruct> MyList;
    TCriticalSection *Lock;
};

TMyThread *Thread = NULL;

...

__fastcall TMyThread::TMyThread()
    : TThread(false)
{
    Lock = new TCriticalSection;
}

__fastcall TMyThread::~TMyThread()
{
    delete Lock;
}

void __fastcall TMyThread::Execute()
{
    TMyStruct Data;
    ...
    Lock->Enter();
    try {
        MyList.push_back(Data);
    }
    __finally {
        Lock->Leave();
    }
    ...
}

void __declspec(dllexport) __stdcall StartThread ()
{
    Thread = new TMyThread;
}

void __declspec(dllexport) __stdcall StopThread ()
{
    if (Thread) {
        Thread->Terminate();
        Thread->WaitFor();
        delete Thread;
        Thread = NULL;
    }
}

bool __declspec(dllexport) __stdcall GetMyStruct (int Index, TMyStruct* Data)
{
    if (!Thread) return false;

    Thread->Lock->Enter();
    try {
        *Data = Thread->MyList[Index];
    }
    __finally {
        Thread->Lock->Leave();
    }

    return true;
}

What is the best/prefered/common way to store MyList and access it in a different thread?

Это полностью зависит от вас, исходя из ваших конкретных потребностей и дизайна проекта.

Иногда я склонен слишком много думать... Спасибо за помощь, Реми.

Martin Nijhoff 08.04.2022 08:34
"почему бы вместо этого не использовать std::mutex?" — это то, что я хотел использовать, но, видимо, в моей версии C++Builder XE6 нет std::mutex. Я выполнил поиск файла mutex в папке «include», но не нашел. В подпапке dinkumware есть algorithm ... yvals.h, но нет mutex.
Martin Nijhoff 08.04.2022 08:42

@MartinNijhoff std::mutex был представлен в C++ 11 в заголовке <mutex>. Только компиляторы на основе Clang поддерживают C++11, а не «классический» компилятор Borland. Поэтому убедитесь, что ваш проект настроен соответствующим образом. Компиляторы Clang C++11 были впервые представлены в XE3.

Remy Lebeau 08.04.2022 10:06

Другие вопросы по теме