帮我的代码加个注释
#include <stdio.h>
#include <windows.h>

#define BUFFER_SIZE 5
#define NUM_PRODUCERS 2
#define NUM_CONSUMERS 2

HANDLE emptySlots;
HANDLE fullSlots;
HANDLE mutex;

int buffer[BUFFER_SIZE];
int in = 0;
int out = 0;

void produce(int data) {
    buffer[in] = data;
    in = (in + 1) % BUFFER_SIZE;
}

int consume() {
    int data = buffer[out];
    out = (out + 1) % BUFFER_SIZE;
    return data;
}

void SetCursorPosition(int x, int y) {
    COORD coord;
    coord.X = x;
    coord.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}

DWORD WINAPI producerThread(LPVOID lpParam) {
    int producerId = *(int*)lpParam;
    int producedCount = 0;

    while (1) {
        WaitForSingleObject(emptySlots, INFINITE);
        WaitForSingleObject(mutex, INFINITE);

        int data = rand() % 100 + 1;
        produce(data);
        SetCursorPosition(producedCount % BUFFER_SIZE * 2, BUFFER_SIZE + 3);
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_INTENSITY);
        printf("■ ");
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);

        ReleaseMutex(mutex);
        ReleaseSemaphore(fullSlots, 1, NULL);

        producedCount++;

        if (producedCount % 5 == 0) {
            SetCursorPosition(producedCount / 5 - 1, BUFFER_SIZE + 4);
            printf("■");
        }

        Sleep(rand() % 1000);
    }

    return producedCount;
}

DWORD WINAPI consumerThread(LPVOID lpParam) {
    int consumerId = *(int*)lpParam;
    int consumedCount = 0;

    while (1) {
        WaitForSingleObject(fullSlots, INFINITE);
        WaitForSingleObject(mutex, INFINITE);

        int data = consume();
        SetCursorPosition(consumedCount % BUFFER_SIZE * 2, BUFFER_SIZE + 5);
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_INTENSITY);
        printf("■ ");
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);

        ReleaseMutex(mutex);
        ReleaseSemaphore(emptySlots, 1, NULL);

        consumedCount++;

        Sleep(rand() % 1000);
    }

    return consumedCount;
}

void PrintResourcesCount(int producersCount, int consumersCount) {
    SetCursorPosition(20, BUFFER_SIZE + 3);
    printf("生产者生产资源    ", producersCount);

    SetCursorPosition(20, BUFFER_SIZE + 5);
    printf("消费者消费资源   ", consumersCount);
}

void PrintBufferState() {
    SetCursorPosition(0, 0);
    printf("缓冲区内容: ");
    for (int i = 0; i < BUFFER_SIZE; i++) {
        if (i == out) {
            SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
        }
        printf("■ ");
        if (i == in) {
            SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);
        }
    }
    printf("\n");

    SetCursorPosition(0, 1);
    printf("空槽位数量: %d    \n", BUFFER_SIZE - in + out);

    SetCursorPosition(0, 2);
    printf("已占用槽位数量: %d    \n", in - out);
}

int main() {
    emptySlots = CreateSemaphore(NULL, BUFFER_SIZE, BUFFER_SIZE, NULL);
    fullSlots = CreateSemaphore(NULL, 0, BUFFER_SIZE, NULL);
    mutex = CreateMutex(NULL, FALSE, NULL);

    DWORD producerThreadId[NUM_PRODUCERS];
    HANDLE producerThreads[NUM_PRODUCERS];
    int producerIds[NUM_PRODUCERS];
    for (int i = 0; i < NUM_PRODUCERS; i++) {
        producerIds[i] = i + 1;
        producerThreads[i] = CreateThread(NULL, 0, producerThread, &producerIds[i], 0, &producerThreadId[i]);
    }

    DWORD consumerThreadId[NUM_CONSUMERS];
    HANDLE consumerThreads[NUM_CONSUMERS];
    int consumerIds[NUM_CONSUMERS];
    for (int i = 0; i < NUM_CONSUMERS; i++) {
        consumerIds[i] = i + 1;
        consumerThreads[i] = CreateThread(NULL, 0, consumerThread, &consumerIds[i], 0, &consumerThreadId[i]);
    }

    int producersCount = 0;
    int consumersCount = 0;

    while (1) {
        PrintBufferState();
        PrintResourcesCount(producersCount, consumersCount);

        Sleep(1000);
    }

    WaitForMultipleObjects(NUM_PRODUCERS, producerThreads, TRUE, INFINITE);
    WaitForMultipleObjects(NUM_CONSUMERS, consumerThreads, TRUE, INFINITE);

    for (int i = 0; i < NUM_PRODUCERS; i++) {
        CloseHandle(producerThreads[i]);
    }
    for (int i = 0; i < NUM_CONSUMERS; i++) {
        CloseHandle(consumerThreads[i]);
    }
    CloseHandle(emptySlots);
    CloseHandle(fullSlots);
    CloseHandle(mutex);

    return 0;
}