User 13204940 Ответов: 2

Кошмар получения простой потоковой работы C++


Привет,

Все, что я хочу сделать, это запустить поток в другом классе. Я пробовал так много комбинаций вещей, но всегда есть проблема.

PlayerConnection.cpp:
#include "stdafx.h"
#include "PlayerConnection.h"

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <winsock.h>
#include <vector>

typedef struct
{
	PlayerConnection* instance;
	SOCKET& socket;
} ThreadData;

int PlayerConnection::ThreadMethod(SOCKET& socket)
{// recv etc
	return 0;
	
}

void PlayerConnection::init(SOCKET& socket)
{
	this->socket = socket;

	ThreadData* td;
	td->instance = this;
	td->socket = socket;

	DWORD connection_thread;

	CreateThread(NULL, 0, ThreadProcedure, &td, 0, &connection_thread);
}

int PlayerConnection::ThreadProcedure(LPVOID p)
{
	ThreadData* ptd = (ThreadData*)p;
	return ptd->instance->ThreadMethod(ptd->socket);
}

PlayerConnection.ч:
#pragma once

#include <winsock.h>

class PlayerConnection
{
public:
	void init(SOCKET& socket);
	int ThreadMethod(SOCKET& socket);
	int ThreadProcedure(LPVOID p);

private:
	SOCKET socket;
	
};

Проблемы:

CreateThread(NULL, 0, ThreadProcedure, &td, 0, &connection_thread);

argument of type "int (PlayerConnection::*)(LPVOID p)" is incompatible with parameter of type "LPTHREAD_START_ROUTINE"
и
'PlayerConnection::ThreadProcedure': non-standard syntax; use '&' to create a pointer to member

Что я уже пробовал:

-------------------------------------------------------------

Richard MacCutchan

Сообщение об ошибке совершенно ясно. И вам, вероятно, нужно сделать эту функцию статичной.

[no name]

Если я сделаю эту функцию статической, она не сможет получить доступ ни к одной из переменных-членов. Затем я не могу передать экземпляр класса этому методу, потому что он принимает только LPVOID, а PlayerConnection не может быть приведен к LPVOID.

Это не ясно, потому что если бы это было так, то prepending & исправил бы это, но это не так, тогда это становится

HANDLE CreateThread(LPSECURITY_ATTRIBUTES,SIZE_T,LPTHREAD_START_ROUTINE,LPVOID,DWORD,LPDWORD)': не удается преобразовать аргумент 3 из 'DWORD (__stdcall *)(PlayerConnection)' в 'LPTHREAD_START_ROUTINE

Richard MacCutchan

Это верно, но вы не можете передать адрес функции-члена класса, так как он не может быть вычислен во время компиляции. И любой указатель может быть приведен к LPVOID, если вы понимаете потенциальные проблемы.

[no name]

Все еще отчаянно нуждаясь в ответе.

2 Ответов

Рейтинг:
9

User 13204940

Исправлено использование std::thread вместо этого. Гораздо аккуратнее, современнее и как-то решает этот вопрос. Не совсем уверен, как, но это делает свою работу.

struct ThreadData
{
	PlayerConnection* instance;
	SOCKET& socket;
};
ThreadData td = { this, socket };
std::thread t(receive, td);
t.join();
void receive(ThreadData data)
{
	PlayerConnection* _this = data.instance;
	SOCKET& socket = data.socket;
	...
}


[no name]

Какой же ты жалкий ребенок. Это решило мою проблему, вы не в том положении, чтобы ее преуменьшать.

Рейтинг:
20

Rick York

То, что я обычно делаю в этой ситуации, как упоминал Ричард. Я использую статический метод интерфейса потока в качестве процедуры потока. A создайте другой нестатический метод, который создает поток и передает его в качестве аргумента. Затем он вызывает другой нестатический метод, который выполняет фактическую работу. Вот краткий пример с соответствующими битами :

class CMyObject
{
    int ThreadMethod();   // do the work of the thread in this

    static int ThreadProcedure( pvoid * p )
    {
       CMyObject * pthis = (CMyObject *)p;
       return pthis->ThreadMethod();
    }

    void StartWorkThread()
    {
        // probably have to do some casting here to compile

        BeginThread( ThreadProcedure, this );  // change this to create your thread 
    }
};
Иногда метод thread нуждается в некоторых дополнительных аргументах, поэтому я создаю небольшой класс или структуру для хранения аргументов вместе с указателем this, так как вы можете передать только одну вещь процедуре thread. Вот вам пример этого :
typedef struct
{
   int arg1;
   double arg2;
   CSomethingElse * pse;
   CMyObject * pthis;
} ThreadData;

int CMyObject::ThreadMethod( ThreadData *ptd )
{
   // this was changed to accept the data argument

   ThreadData td;
   memcpy( &td, ptd, sizeof( ThreadData ) );   // make a copy

  // and so on....
}

void CMyObject::StartWorkThread()
{
   ThreadData td;
   td.arg1 = 42;
   td.arg2 = 3.1416;
   td.pse = nullptr;
   td.pthis = this;

   BeginThread( ThreadProcedure, &td );  // change this to create your thread
}

int CMyObject::ThreadProcedure( pvoid *p )
{
   ThreadData * ptd = (ThreadData *)p;
   return ptd->pthis->ThreadMethod( ptd );
}
Вы также можете передать данные потока в ThreadMethod. Копия создается потому, что данные потока будут недействительны при возврате StartWorkThread.


[no name]

Вы это проверяли?

структура typedef
{
PlayerConnection* экземпляр;
Розетка& розетка;
} ThreadData;

void PlayerConnection::init(SOCKET& socket)
{
это->socket = сокет;

Данные ThreadData;
data.instance = это;
данных.гнездо = гнездо;
CreateThread(receive_thread, (LPVOID)данные);
}

Ошибка на receive_thread: argument of type "unsigned int (*)(ThreadData data)" is incompatible with parameter of type "LPSECURITY_ATTRIBUTES"
Ошибка в данных ThreadData: the default constructor of "ThreadData" cannot be referenced -- it is a deleted function
Ошибка в данных (LPVOID): no suitable conversion function from "ThreadData" to "LPVOID" exists

Даже если я изменю его на ThreadData* data, Too few arguments in function call для CreateThread.

Rick York

Нет, я не проверял это, потому что я делаю это все время. Это работает. Вы читали комментарий там? Он говорит: "вероятно, придется сделать здесь какое-то литье, чтобы скомпилировать." У меня есть небольшая функция, которая упрощает этот материал для меня, что является общим для тех, кто много работает с потоками.

Если вы собираетесь передать данные, то это должно быть сделано с помощью указателя, так что это означает, что вам нужен оператор &, чтобы получить его адрес. Кроме того, дайте вызов CreateThread соответствующие аргументы, и вы должны быть почти там. Ошибка, которую я допустил, заключалась в том, что моя функция называется BeginThread, и я должен был использовать ее. Он имеет дело с атрибутами безопасности и размером стека. Я обновлю свой пост, чтобы использовать его.

[no name]

BeginThread не определен. Посмотрите на исходный пост, я обновил его до своего самого последнего кода.

Rick York

Вы, очевидно, не читали мой ответ. Это последний раз, когда я так трачу свое время.

[no name]

Вдобавок ко всему этому беспорядку я даже не могу использовать файл .h сейчас, потому что мне нужно объявить ThreadData в PlayerConnection.h, но он содержит член типа PlayerConnection*. Если я перемещаю ThreadData в другой файл, возникает циклическая зависимость, потому что он нуждается в playerconnection, но для этого требуется threaddata.h.

0x01AA

Мое мнение: прежде чем вы попробуете использовать потоки, вы должны быть в безопасности с основами. Средства, среди прочего, как предотвратить циклические зависимости....

[no name]

Я собираюсь обновить вопрос с помощью кода, который у меня сейчас есть. Это заставило меня чувствовать себя таким опустошенным и пресыщенным. Я не знаю никакого способа справиться с циклическими зависимостями, кроме как предотвратить их в первую очередь.

Jochen Arndt

Возразил один голос с 5 потому что это ответ:
Функция потока должен будьте статичны, и передача указателя на класс позволяет получить доступ к членам класса, который включает вызов нестатической версии функции потока.

Лучший метод, чем расширенный пример, заключается в том, чтобы по-прежнему передавать указатель на экземпляр класса и реализовывать дополнительные параметры как (обычно частные) члены класса, а не передавать структуру. Затем эти члены могут быть доступны с помощью нестатической функции потока или с помощью указателя класса в статической функции.

Rick York

Я иногда делаю это, но я не решаюсь добавлять членов в класс только для этой цели. Я думаю, что это сводится к вопросу предпочтения в этом вопросе.

Спасибо за голосование.