タスクにソケットが含まれている場合、std::async を使用してタスクを並列実行するのに問題があります。
私のプログラムは、Linux 用の標準 C++ で書かれた単純な TCP ソケット サーバーです。クライアントが接続すると、専用のポートが開かれ、個別のスレッドが開始されるため、各クライアントは独自のスレッドで処理されます。
クライアント オブジェクトはマップに含まれています。
すべてのクライアントにメッセージをブロードキャストする機能があります。私はもともと以下のように書いていました:
// ConnectedClient is an object representing a single client
// ConnectedClient::SendMessageToClient opens a socket, connects, writes, reads response and then closes socket
// broadcastMessage is the std::string to go out to all clients
// iterate through the map of clients
map<string, ConnectedClient*>::iterator nextClient;
for ( nextClient = mConnectedClients.begin(); nextClient != mConnectedClients.end(); ++nextClient )
{
printf("%s\n", nextClient->second->SendMessageToClient(broadcastMessage).c_str());
}
これをテストしたところ、一度に 3 つのクライアントで動作します。メッセージは 3 つのクライアントすべてに (一度に 1 つずつ) 届き、応答文字列はこのループで 3 回出力されます。ただし、メッセージは一度に 1 つのクライアントにしか送信されないため、低速です。
より効率的にするために、std::async を利用して、すべてのクライアントに対して SendMessageToClient 関数を非同期的に呼び出すことを望んでいました。上記のコードを次のように書き直しました。
vector<future<string>> futures;
// iterate through the map of clients
map<string, ConnectedClient*>::iterator nextClient;
for ( nextClient = mConnectedClients.begin(); nextClient != mConnectedClients.end(); ++nextClient )
{
printf("start send\n");
futures.push_back(async(launch::async, &ConnectedClient::SendMessageToClient, nextClient->second, broadcastMessage, wait));
printf("end send\n");
}
vector<future<string>>::iterator nextFuture;
for( nextFuture = futures.begin(); nextFuture != futures.end(); ++nextFuture )
{
printf("start wait\n");
nextFuture->wait();
printf("end wait\n");
printf("%s\n", nextFuture->get().c_str());
}
上記のコードは、マップ内にクライアントが 1 つしかない場合に期待どおりに機能します。「送信開始」に続いて「送信終了」がすぐに続き、すぐに「待機開始」が続き、3 秒後に (これをテストするためにクライアント応答側で 3 秒間スリープします)、ソケットからのトレースが表示されます。関数を読んで、応答が来ることを確認すると、「end wait」と表示されます
問題は、マップに複数のクライアントがある場合です。ソケットを開いて接続する SendMessageToClient 関数の部分で、以下のコードで失敗します。
// connected client object has a pipe open back to the client for sending messages
int clientSocketFileDescriptor;
clientSocketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0);
// set the socket timeouts
// this part using setsockopt is omitted for brevity
// host name
struct hostent *server;
server = gethostbyname(mIpAddressOfClient.c_str());
if (server == 0)
{
close(clientSocketFileDescriptor);
return "";
}
//
struct sockaddr_in clientsListeningServerAddress;
memset(&clientsListeningServerAddress, 0, sizeof(struct sockaddr_in));
clientsListeningServerAddress.sin_family = AF_INET;
bcopy((char*)server->h_addr, (char*)&clientsListeningServerAddress.sin_addr.s_addr, server->h_length);
clientsListeningServerAddress.sin_port = htons(mPortNumberClientIsListeningOn);
// The connect function fails !!!
if ( connect(clientSocketFileDescriptor, (struct sockaddr *)&clientsListeningServerAddress, sizeof(clientsListeningServerAddress)) < 0 )
{
// print out error code
printf("Connected client thread: fail to connect %d \n", errno);
close(clientSocketFileDescriptor);
return response;
}
出力は次のとおりです。「接続されたクライアント スレッド: 接続に失敗しました 4」。
このエラーコードを調べたところ、次のように説明されています。
#define EINTR 4 /* Interrupted system call */
私はインターネット上で検索しましたが、システムコールが信号によって中断されているという言及がいくつか見つかりました。
一度に1つずつメッセージ送信関数を呼び出すとこれが機能するのに、非同期を使用してメッセージ送信関数を呼び出すと失敗する理由を誰かが知っていますか? 複数のクライアントにメッセージを送信する方法について、別の提案がある人はいますか?