Чтение нескольких потоков из одного файла

Моя платформа - Windows Vista 32, с Visual C++ Express 2008 .

например:

если у меня есть файл, содержащий 4000 байтов, могу ли я прочитать 4 потока из файла одновременно? и каждый поток обращается к отдельному разделу файла.

нить 1 читается 0-999, нить 2 читается 1000 - 2999 и т. д.

Пожалуйста, приведите пример на языке Си.

13 ответов

Решение

Если вы не пишете им, не нужно заботиться о состоянии синхронизации / гонки.

Просто откройте файл с общим чтением, как разные ручки, и все будет работать. (т. е. вы должны открыть файл в контексте потока, а не использовать тот же дескриптор файла).

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

DWORD WINAPI mythread(LPVOID param)
{
    int i = (int) param;
    BYTE buf[1000];
    DWORD numread;

    HANDLE h = CreateFile("c:\\test.txt", GENERIC_READ, FILE_SHARE_READ,
        NULL, OPEN_EXISTING, 0, NULL);

    SetFilePointer(h, i * 1000, NULL, FILE_BEGIN);
    ReadFile(h, buf, sizeof(buf), &numread, NULL); 
    printf("buf[%d]: %02X %02X %02X\n", i+1, buf[0], buf[1], buf[2]);

    return 0;
}

int main()
{
    int i;
    HANDLE h[4];

    for (i = 0; i < 4; i++)
        h[i] = CreateThread(NULL, 0, mythread, (LPVOID)i, 0, NULL);

    // for (i = 0; i < 4; i++) WaitForSingleObject(h[i], INFINITE);
    WaitForMultipleObjects(4, h, TRUE, INFINITE);

    return 0;
}

Честно говоря, нет даже большой проблемы с записью в один и тот же файл.

Безусловно, самый простой способ - просто отобразить файл в памяти. Затем ОС выдаст вам void*, где файл отображается в памяти. Приведите это к char[] и убедитесь, что каждый поток использует неперекрывающиеся подмассивы.

void foo(char* begin, char*end) { /* .... */ }
void* base_address = myOS_memory_map("example.binary");
myOS_start_thread(&foo, (char*)base_address, (char*)base_address + 1000);
myOS_start_thread(&foo, (char*)base_address+1000, (char*)base_address + 2000);
myOS_start_thread(&foo, (char*)base_address+2000, (char*)base_address + 3000);

Windows поддерживает перекрывающиеся операции ввода-вывода, что позволяет одному потоку асинхронно ставить в очередь несколько запросов ввода-вывода для повышения производительности. Вероятно, это может быть использовано несколькими потоками одновременно, если файл, к которому вы обращаетесь, поддерживает поиск (т. Е. Это не канал).

Переходя FILE_FLAG_OVERLAPPED в CreateFile() позволяет одновременное чтение и запись на один и тот же дескриптор файла; в противном случае Windows сериализует их. Укажите смещение файла, используя Offset а также OffsetHigh члены OVERLAPPED состав.

Для получения дополнительной информации см. Синхронизация и перекрывающиеся входы и выходы.

Как уже отмечали другие, не существует внутренней проблемы чтения нескольких потоков из одного и того же файла, если у них есть свой дескриптор / дескриптор файла. Тем не менее, мне немного любопытно ваши мотивы. Почему вы хотите прочитать файл в параллель? Если вы только читаете файл в память, скорее всего, узким местом является сам диск, и в этом случае несколько потоков вам совсем не помогут (это просто загромождает ваш код).

И как всегда при оптимизации, вы не должны пытаться делать это, пока у вас (1) не будет легкого для понимания, работы, решения и (2) вы измерили свой код, чтобы узнать, где вы должны оптимизировать.

Самый простой способ - открыть файл в каждом параллельном экземпляре, но просто открыть его только для чтения.

Люди, которые говорят, что может быть узкое место IO, вероятно, ошибаются. Любая современная операционная система кеширует файл, читает. Это означает, что первое чтение файла будет самым медленным, а последующее чтение будет молниеносным. 4000-байтовый файл может даже находиться внутри кеша процессора.

Вы можете, конечно, иметь несколько потоков, читающих из структуры данных, условия гонки могут потенциально возникнуть, если происходит какая-либо запись.

Чтобы избежать таких условий гонки, вам нужно определить границы, которые могут прочитать потоки, если у вас есть явное количество сегментов данных и явное количество потоков, чтобы соответствовать этим, то это легко.

Что касается примера на C, вам нужно будет предоставить некоторую дополнительную информацию, например, библиотеку потоков, которую вы используете. Попробуйте сначала, а затем мы можем помочь вам решить любые проблемы.

Я не вижу никакого реального преимущества в этом.
У вас может быть несколько потоков, считывающих данные с устройства, но узким местом будет не процессор, а скорость дискового ввода-вывода.

Если вы не будете осторожны, вы можете даже замедлить процессы (но вам нужно будет измерить это, чтобы знать наверняка).

Это возможно, хотя я не уверен, что это будет стоить усилий. Рассматривали ли вы чтение всего файла в память в пределах одного потока, а затем разрешить нескольким потокам доступ к этим данным?

Вам не нужно делать что-то особенно умное, если все, что они делают, это чтение. Очевидно, что вы можете читать его столько раз параллельно, сколько захотите, если только вы не блокируете его. Писать это явно другое дело, конечно...

Мне, правда, интересно, почему вы этого хотите - он, вероятно, будет работать плохо, поскольку ваш жесткий диск будет тратить много времени на поиск назад и вперед, а не на чтение всего за один (относительно) непрерывный цикл. Для небольших файлов (например, в вашем примере с 4000 строками), где это может не быть такой проблемой, это, похоже, не стоит этой проблемы.

Чтение: нет необходимости блокировать файл. Просто откройте файл только для чтения или для совместного использования

Запись: используйте мьютекс, чтобы гарантировать, что файл будет записан только одним человеком.

std::mutex mtx;

void worker(int n)
{
    mtx.lock();

    char * memblock;

    ifstream file ("D:\\test.txt", ios::in);

    if (file.is_open())
    {
        memblock = new char [1000];
        file.seekg (n * 999, ios::beg);
        file.read (memblock, 999);
        memblock[999] = '\0';

        cout << memblock << endl;

        file.close();
        delete[] memblock;
    }
    else 
        cout << "Unable to open file";
    mtx.unlock();
}


int main()
{
    vector<std::thread> vec;
    for(int i=0; i < 3; i++)
    {
        vec.push_back(std::thread(&worker,i));
    }

    std::for_each(vec.begin(), vec.end(), [](std::thread& th)
    {
        th.join();
    });
    return 0;
}

Вам нужен способ синхронизации этих потоков. Существуют разные решения мьютекса http://en.wikipedia.org/wiki/Mutual_exclusion

Он хочет читать из файла в разных темах. Я думаю, что все должно быть в порядке, если файл открыт только для чтения каждым потоком.

Я надеюсь, что вы не хотите делать это для производительности, так как вам придется сканировать большие части файла на наличие символов новой строки в каждом потоке.

Другие вопросы по тегам