Как правильно передать аргументы функции в CreateThread
Здравствуйте меня интересует как передать аргументы функции передаваемой в CreateThread
Я использую CreateThread(NULL,0,LPTHREAD_START_ROUTINE(func),NULL,0,NULL) я понял что мне нужно использовать ThreadProc но я совершенно не понимаю как с ней работать, я был бы очень благодарен если-бы привели пример
Ответы (3 шт):
Ну, вот пример, как его вызвать (упрощенный, без проверок). Тут же второй пример, как это же сделать, не заморачиваясь работой с API, а только средствами C++.
DWORD WINAPI Thread1(void*)
{
for(int i = 0; i < 20; ++i)
{
cout << i;
Sleep(10);
}
return 0;
}
void Thread2()
{
for(int i = 0; i < 20; ++i)
{
cout << char('A'+i);
Sleep(10);
}
}
int main([[maybe_unused]] int argc,
[[maybe_unused]] const char * argv[])
{
CreateThread(0,4096,Thread1,0,0,NULL);
thread th(Thread2);
th.detach();
for(int i = 0; i < 20; ++i)
{
cout << '-';
Sleep(10);
}
}
Можете пользоваться тем, что больше по душе :)
4 параметр как раз получает указатель на нужные для потока данные. Давайте попробуем обычный int туда пробросить (код немного не для промышленного использования, а только для понимания)
#include <iostream>
#include <windows.h>
using namespace std;
DWORD WINAPI MyThread(void* p)
{
int loop = (int)p; // вернем тип назад
for(int i = 0; i < loop; ++i)
{
cout << i;
Sleep(1);
}
return 0;
}
int main(int argc, char * argv[])
{
int loop = 7; // наш параметр
CreateThread(0,4096,MyThread,(void*)loop,0,NULL);
// скастим и передадим ^^^^^^^^^^^
for(int i = 0; i < 20; ++i)
{
cout << '-';
Sleep(1);
}
}
Выглядит дико, но на то он и void*. В рабочем коде этим параметром передавайте указатель на какую-нибудь структуру, созданную динамически (если это будет указатель на структуру на стеке, тогда нужно побеспокоится, что бы поток "жил меньше, чем до выхода из вызывающего скоупа". В случае динамической структуры также нужно решить, кто и когда будет чистить память. Есть такие варианты:
- поток сам почистит (в начале своей работы или в конце)
- поток в этой структуре оставит "свои расчеты", и вызывающий поток подхватит их.
- java-стайл - операционнка за нами подчистит память.
В целом, первый вариант выглядит где то так.
#include <iostream>
#include <string>
#include <windows.h>
using namespace std;
// это структура для связи
struct mydata
{
int loop;
string message;
};
DWORD WINAPI MyThread(void* p)
{
mydata * d = (mydata*)p; // конвертируем указатель в правильный
for(int i = 0; i < d->loop; ++i)
{
cout << d->message << " " << i;
Sleep(1);
}
delete d; // чистим, когда оно нам не нужно
return 0;
}
int main(int argc, char * argv[])
{
mydata * d = new mydata{7, "hello"}; // создаем экземпляр для работы
CreateThread(0,4096,MyThread,(void*)d,0,NULL);
for(int i = 0; i < 20; ++i)
{
cout << '-';
Sleep(1);
}
}
конечно, этому коду нужно подобавлять всякие проверки на то, что поток создался и подобное, но в целом - это уже рабочий код.
Вот более-менее полноценный пример:
#include <Windows.h>
#include <exception>
#include <iostream>
#include <memory>
#include <cassert>
#include <cstdlib>
#include <cstddef>
struct
t_ThreadContext
{
char const * psz_message;
::std::exception_ptr p_exception;
};
DWORD WINAPI
thread_proc(LPVOID const param) noexcept
{
assert(param);
DWORD exit_code{ERROR_INVALID_FUNCTION};
auto & context{*static_cast<t_ThreadContext *>(param)};
assert(context.psz_message);
try
{
// do some work...
::std::cout << context.psz_message << ::std::endl;
exit_code = ERROR_SUCCESS;
}
catch (...)
{
// intersept exception so it can be inspected in parent thread
context.p_exception = ::std::current_exception();
exit_code = ERROR_INVALID_FUNCTION;
}
if (STILL_ACTIVE == exit_code)
{
exit_code = ERROR_INVALID_FUNCTION;
}
return exit_code;
}
int
main([[maybe_unused]] int const argc, [[maybe_unused]] char const * const * const pp_args)
{
int exit_code{EXIT_FAILURE};
try
{
auto const & psz_command{pp_args[0]};
::t_ThreadContext context{psz_command};
LPSECURITY_ATTRIBUTES p_security_attributes{}; // nullptr - default security
DWORD stack_size{}; // 0 - default
DWORD flags{}; // 0 - default (create thread and run it immediately)
DWORD thread_id{};
auto const h_thread
{
::CreateThread
(
p_security_attributes
, stack_size
, ::std::addressof(::thread_proc)
, static_cast<LPVOID>(::std::addressof(context))
, flags
, ::std::addressof(thread_id) // may be nullptr
)
};
if (not h_thread)
{
auto const last_error{::GetLastError()};
// deal with error...
}
else
{
auto const wait_result{::WaitForSingleObject(h_thread, INFINITE)};
if (WAIT_OBJECT_0 != wait_result)
{
auto const last_error{::GetLastError()};
// error here is most likely fatal...
}
else
{
if (not ::CloseHandle(h_thread))
{
auto const last_error{::GetLastError()};
// error here is most likely fatal...
}
else
{
// inspect exception occurred in child thread
if (context.p_exception)
{
::std::rethrow_exception(context.p_exception);
}
exit_code = EXIT_SUCCESS;
}
}
}
}
catch (...)
{
exit_code = EXIT_FAILURE;
}
return exit_code;
}
Пояснения:
- Пользовательские данные можно передавать посредством непрозрачного указателя
void *, который передается четвертым аргументом вCreateThread, а затем пробрасывается в процедуру потока. - Процедура потока, как разные другие С функции обратного вызова из WinAPI, обязана не допускать проброса С++ исключений через границы языка. Поэтому ее имеет смысл объявлять как
noexcept, а возникающие исключения перехватывать и либо сразу обрабатывать, либо передавать для обработки в главный поток посредством::std::exception_ptr. - Значение, возвращаемое процедурой потока, может быть произвольным, однако имеет смысл использовать коды ошибок Windows. Единственно исключение - в качестве кода возврата не должно использоваться значение
STILL_ACTIVE. - Типичной ошибкой является игнорирование значений, возвращаемых функциями WinAPI.
- Типичной ошибкой является игнорирование ожидания завершения потока.
- Типичной ошибкой является игнорирование необходимости закрывать дескриптор потока.
- По сравнению с библиотечным классом
::std::thread, создание потоков посредством WinAPI может быть оправдано, когда требуется задавать нужный размер стека или задавать атрибуты безопасности.