如何创建未定义数量的线程并在窗口c上使用WaitForMultipleObjects()
问题描述:
PS:我对线程很陌生。如何创建未定义数量的线程并在窗口c上使用WaitForMultipleObjects()
我有一个问题,我需要等待来自客户端的连接请求(完全任意的次数),接受套接字上的连接,连接后创建工作线程。创建的线程然后创建一个char数组,在其上工作并需要将其传递给父进程。
我已经能够创建在while循环的螺纹等
while ((new_socket = accept(srv_sock, (struct sockaddr *)&client, &c)) != INVALID_SOCKET)
{
puts("\nConnection accepted");
_beginthreadex(0, 0, handle_client, &new_socket, 0, 0);
}
我已经看到,pthread_join()
可用于从线程父进程(在UNIX中)传递数据。我的问题是,我如何将它整合到主流程的循环中。 我希望采用以下方法将导致客户端和服务器之间每次只能建立一个以上的连接,这是不需要的。
while ((new_socket = accept(srv_sock, (struct sockaddr *)&client, &c)) != INVALID_SOCKET)
{
puts("\nConnection accepted");
_beginthreadex(0, 0, handle_client, &new_socket, 0, 0);
pthread_join(thread_id,&my_array);
}
编辑:我会很高兴知道如果我想是不可能的或者有替代pthread_join().
或其等价物的窗户。
编辑:我知道pthread_join()
是Unix的,并且已经读了WaitForMultipleObjects()
是它的等效窗口。无论如何,我仍然无法找出解决方案。
答
我已经看到,可以使用pthread_join()将数据从线程传递给父进程。
这不完全正确。你可以通过一个指针,当你退出一个线程,并使用pthread_join收集该指针。你必须自己实现所有的逻辑。 API不知道(或关心)指针是什么。线程没有父母和孩子,他们是兄弟姐妹。
实施例用于创建者和收割者:
-
全球
struct VarLengthArray { size_t count; MyElem data[1]; };
-
退出螺纹:
// allocate the result size_t count = ...; VarLengthArray *retval = malloc( sizeof(VarLengthArray) + sizeof(MyElem) * (count > 0 ? count - 1 : 0) ); // fill the result retval->count = count; for (size_t i = 0; i < retval->count; ++i) { retval->data[i] = ...; } pthread_exit(retval);
-
收集线程:
// collect the result void *retval_; if (pthread_join(thread_one_id, &retval_) != 0) { // handle error } VarLengthArray *retval = retval_; // use the result for (size_t i = 0; i < retval->count; ++i) { printf("retval->[%u] = %s\n", (unsigned) i, retval->data[i].string_value); } // deallocate the result free(retval);
使用条件变量和众多创作者的完整的例子:
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
typedef struct Datum {
struct Datum *next;
char some_data[32];
} Datum;
typedef struct SharedData {
pthread_mutex_t mutex;
pthread_cond_t cond_empty;
unsigned seed;
Datum *head, *tail;
unsigned children_alive;
} SharedData;
static void *thread_logic(void *argv_);
int main(int argc, char **argv) {
unsigned thread_count = 2;
if (argc > 1) {
if (sscanf(argv[1], " %u ", &thread_count) != 1) {
fprintf(stderr, "Usage: %s [thread_count]\n", argv[0]);
return 1;
}
}
// initialize shared data
SharedData shared_data;
pthread_mutex_init(&shared_data.mutex, NULL);
pthread_cond_init(&shared_data.cond_empty, NULL);
shared_data.seed = time(NULL);
shared_data.head = NULL;
shared_data.tail = NULL;
shared_data.children_alive = 0;
// start threads detached, so you don't have to call pthread_join
pthread_t *child_ids = malloc(sizeof(pthread_t) * thread_count);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// start the threads
pthread_mutex_lock(&shared_data.mutex);
for (unsigned i = 0; i < thread_count; ++i) {
if (pthread_create(&child_ids[i], &attr, thread_logic, &shared_data) != 0) {
perror("pthread_create");
} else {
++shared_data.children_alive;
}
}
pthread_mutex_unlock(&shared_data.mutex);
pthread_attr_destroy(&attr);
// loop until all threads are dead
while (shared_data.children_alive > 0) {
// a condition variable: wait until there is data you can read
pthread_mutex_lock(&shared_data.mutex);
while (shared_data.head == NULL) {
pthread_cond_wait(&shared_data.cond_empty, &shared_data.mutex);
}
// collect a first datum
Datum *datum = shared_data.head;
if (datum->next != NULL) {
shared_data.head = datum->next;
} else {
shared_data.head = shared_data.tail = NULL;
}
pthread_mutex_unlock(&shared_data.mutex);
// handle the data (outside of the mutex lock)
printf("Got data: %s\n", datum->some_data);
free(datum);
}
return 0;
}
static void *thread_logic(void *shared_data_) {
SharedData *shared_data = shared_data_;
while (1) {
pthread_mutex_lock(&shared_data->mutex);
// create some data
useconds_t timeout = (
(((float) (unsigned) rand_r(&shared_data->seed))/UINT_MAX) *
1000000
);
Datum *datum = malloc(sizeof(Datum));
datum->next = NULL;
if (timeout < 1000000/25) {
--shared_data->children_alive;
snprintf(datum->some_data, sizeof(datum->some_data), "I'm done\n");
} else {
snprintf(
datum->some_data, sizeof(datum->some_data),
"Sleeping for %uus\n", timeout
);
}
// append the datum
if (shared_data->head) {
shared_data->tail->next = datum;
} else {
shared_data->head = datum;
pthread_cond_signal(&shared_data->cond_empty);
}
shared_data->tail = datum;
pthread_mutex_unlock(&shared_data->mutex);
// most likely it takes some time to create the data
// do lengthly tasks outside of the mutex lock
if (timeout < 1000000/25) {
return NULL;
} else {
usleep(timeout);
}
}
}
在这样的“窗口”的方法是,我想,等一个IOCP完成端口对于客户端连接接受,(重叠的AcceptEx)以及来自客户端线程的排队消息(PostQueuedCompletionStatus)。或者,在一个不同的线程中运行Accept()循环,并在生产者 - 消费者队列(可能是PostMessage,GetMessage)中将所有内容排队到你的'主线程'(或任何将要管理它的任何东西)。 –
TBH,'join()'在所有情况下都是PITA的一部分,如果可以的话,你应该尽量避免它。如果您可以设计以便不必等待线程完成,那么您将有更简单的多线程编程时间并避免很多痛苦。任何依靠计算线程数量的设计都是一样的 - 设计无所谓,并避免麻烦。 –