【问题标题】:how to create undefined number of threads and use WaitForMultipleObjects() in c on windows如何在 Windows 上的 c 中创建未定义数量的线程并使用 WaitForMultipleObjects()
【发布时间】:2018-03-06 04:00:35
【问题描述】:

PS:我对线程很陌生。

我有一个问题,我需要等待来自客户端的连接请求(完全任意次数),接受套接字上的连接,连接后创建一个工作线程。创建的线程然后创建一个 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(). 或其 Windows 等效项的替代品。

编辑:我知道pthread_join() 是用于 Unix 并且已经读到 WaitForMultipleObjects() 是它的 windows 等价物。无论如何,我仍然无法找到解决方案。

【问题讨论】:

  • 我猜,“Windows”的做法是在 IOCP 完成端口上等待客户端连接接受(重叠的 AcceptEx)和来自客户端线程的排队消息,( PostQueuedCompletionStatus)。或者,在不同的线程中运行 Accept() 循环,并将所有内容排队到您的“主线程”,(或任何将管理这一切的线程),在生产者-消费者队列(可能是 PostMessage、GetMessage)上。
  • TBH,'join()' 在所有情况下都有点像 PITA,如果可以的话,你应该尽量避免它。如果您可以设计为不必等待线程完成,那么您将更轻松地进行多线程编程并避免很多痛苦。任何依赖计算线程数的设计也是如此 - 设计无所谓,避免麻烦。

标签: c windows multithreading winsock waitformultipleobjects


【解决方案1】:

我已经看到 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);
        }
    }
}

【讨论】:

  • 如何在循环中使用 pthread_join()?而且我也不知道创建了多少线程。
  • @George,我会更新我的答案,但这需要几分钟。完成后我会 ping 你。 :)
  • 我认为我提出问题的方式具有误导性。对不起。我正在寻找 Windows 解决方案,我无法提前知道将创建的线程数。
猜你喜欢
  • 2015-09-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多