如何同步以下方案

时间:2015-08-29 17:30:59

标签: c winapi synchronization

这是我想要做的最小例子。现在你会看到,如果你运行这个,那么这个例子需要10秒多一点才能完成。它应该不到2秒。问题是存在竞争条件。循环过长,SetEvent发生在WaitForSingle对象可以获取之前。如果事件可以触发但WaitForSingleObject仍然可以知道它以某种方式触发,那将是多么好的。 这里发生的是生成的数据可能需要很长时间。然后,该数据将通过网络发送,这可能需要更长时间。因此,我希望将要发送的数据排队,然后以另一个线程获取数据并将其发送出去。这样我就可以无限排队,直到我什么也没做,然后线程就会加入发送线程,直到它完成发送所有网络数据为止。

#include <stdio.h>
#include <windows.h>
#include <unistd.h>

#define PIPE_NAME "\\\\.\\pipe\\testpipe"

void copy_protocol_buffer(struct protocol_buffer *in, struct protocol_buffer *out);
DWORD PipeClientStartSendBufferThread(struct client_pipe_settings *pipe_settings, LPDWORD lpThreadId);
DWORD InitializeClientPipeSettings(struct client_pipe_settings *pipe_settings);
void initialize_protocol_buffer(struct protocol_buffer *protocol_buffer);

struct protocol_buffer {

    size_t length;
    size_t location;
    int data_type;
    char *data;
    struct protocol_buffer *next;
};

struct client_pipe_settings {
    HANDLE hPipe;
    LPCTSTR name;
    DWORD pipe_timeout;
    HANDLE write_event;
    struct protocol_buffer *fifo;
    HANDLE send_thread;
};


DWORD WINAPI PipeClientSendThread(LPVOID client_pipe_settings_object) {

    struct client_pipe_settings *pipe_settings = (struct client_pipe_settings *)client_pipe_settings_object;

    struct protocol_buffer *buf = NULL;

    while(1) {
        WaitForSingleObject(pipe_settings->write_event, 10000);

        if (buf == NULL) {
            buf = pipe_settings->fifo;
        } else {
            struct protocol_buffer *fifo_protocol_buffer = buf->next;
            free(buf);
            buf = fifo_protocol_buffer;

            if(buf->length == 0)
                //signal to quit
                return 0;
        }

        //Send data over the network
        Sleep(500);
    }

    return 0;
}

DWORD PipeQueueBuffer(struct client_pipe_settings *pipe_settings, struct protocol_buffer *buf)
{
    struct protocol_buffer *out_protocol_buffer = (struct protocol_buffer *)malloc(sizeof *buf);

    if(out_protocol_buffer == NULL)
        exit(1);

    copy_protocol_buffer(buf, out_protocol_buffer);

    if (pipe_settings->fifo == NULL) {
        pipe_settings->fifo = out_protocol_buffer;
    }
    else
    {
        struct protocol_buffer *last_protocol_buffer = pipe_settings->fifo;
        while(last_protocol_buffer->next != NULL)
        {
            last_protocol_buffer = last_protocol_buffer->next;
        }
        last_protocol_buffer->next = out_protocol_buffer;
    }

    if(!SetEvent(pipe_settings->write_event))
        return GetLastError();

    return ERROR_SUCCESS;
}



int main(void) {
    struct client_pipe_settings pipe_settings;
    InitializeClientPipeSettings(&pipe_settings);

    DWORD dwThreadId = 0;

    PipeClientStartSendBufferThread(&pipe_settings, &dwThreadId);

    //Generate data which could take a while
    Sleep(1000);
    struct protocol_buffer buf;

    initialize_protocol_buffer(&buf);

    buf.length = 5;
    buf.data = (char *)malloc(5);
    buf.data[0] = 'b';
    buf.data[1] = 'l';
    buf.data[2] = 'a';
    buf.data[3] = 'h';
    buf.data[4] = '\0';

    PipeQueueBuffer(&pipe_settings, &buf);

    Sleep(100);
    PipeQueueBuffer(&pipe_settings, &buf);

    buf.length = 0;

    PipeQueueBuffer(&pipe_settings, &buf);

    WaitForSingleObject(pipe_settings.send_thread, 100000);

}

DWORD InitializeClientPipeSettings(struct client_pipe_settings *pipe_settings)
{
    pipe_settings->write_event = CreateEvent(NULL, 0, 0, NULL);
    if(pipe_settings->write_event == NULL)
        return GetLastError();

    pipe_settings->hPipe = INVALID_HANDLE_VALUE;
    pipe_settings->fifo = NULL;

    pipe_settings->send_thread = NULL;


    return ERROR_SUCCESS;
}

DWORD PipeClientStartSendBufferThread(struct client_pipe_settings *pipe_settings, LPDWORD lpThreadId)
{
    HANDLE h = CreateThread(NULL, 0, PipeClientSendThread, pipe_settings, 0, lpThreadId);

    if(h == NULL)
        return GetLastError();

    pipe_settings->send_thread = h;

    return ERROR_SUCCESS;
}

void copy_protocol_buffer(struct protocol_buffer *in, struct protocol_buffer *out) {
    out->data_type = in->data_type;
    out->length = in->length;
    out->location = in->location;
    out->next = in->next;

    out->data = (char*)malloc(in->length);
    if (out->data == NULL) {
        exit(1);
    }

    memcpy(out->data, in->data, in->length);
}

void initialize_protocol_buffer(struct protocol_buffer *protocol_buffer)
{
    protocol_buffer->data = NULL;
    protocol_buffer->length = 0;
    protocol_buffer->location = 0;
    protocol_buffer->next = NULL;
    protocol_buffer->data_type = 0;
}

1 个答案:

答案 0 :(得分:1)

你的机制完全错了。它不是关于早期的SetEvent。

如果设置了“事件”,则可能会“设置多次”。 PipeClientSendThread应该等待事件,如果设置了事件,它应该发送到达队列的所有元素。您将3个元素编码到队列中,但事件设置一次,线程运行并且一次只发送一个元素,而下一个元素仅在达到超时时发送....

你也有一个大问题。您的队列必须受关键部分或互斥锁的保护。您修改并循环队列中的元素,而另一个线程也在读取和修改队列。

使用crtical section和std :: queue ...这也可以帮助你摆脱内存free / malloc的东西。