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

моя платформа-windows vista 32 с visual C++ express 2008 .

например:

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

резьба 1 читать 0-999, резьба 2 читать 1000 - 2999 и т. д.

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

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;
}

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

безусловно, самый простой способ - просто сопоставить файл с памятью. ОС затем даст вам пустоту* , где файл отображается в память. Приведение к 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);

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

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

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


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

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


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

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

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


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

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


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

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


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


чтение: нет необходимости блокировать файл. Просто откройте файл как read only или shared read

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


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

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


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] = '';

        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;
}

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


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

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