Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

VirtualAlloc() и многопоточность

7.2K
31 июля 2008 года
polaroid
94 / / 05.07.2008
Не знал куда запостить, решил что все-таки больше подходит к этой теме. Вообщем моя задача - сделать многопоточное приложение, причем каждый поток должен считывать и парсить данные, пришедшие с сокета. Т.к. требуемый протокол - Http, я использую в качестве буфера указатель на память, а саму память выделяю с помощью VirtualAlloc(). Память должна выделяться в каждом потоке, поэтому перед входом в поток (из основной программы) я вызываю VirtualAlloc() с флагом MEM_RESERVE (т.е. резервирую память, в моем слуае - 20мб), а в самих потоках уже VirtualAlloc с флагом MEM_COMMIT. Все бы хорошо, но! Когда я создаю поток, VirutalAlloc возвращает NULL! Если тестировать программу без потоков, то все в порядке. Подскажите, как решить проблему. Я так подозреваю, что это возможно особенность архитектуры Windows, но материалов про это в сети не нашел.
З.Ы. Язык C (не C++)

Код:

поток:
Код:
void thread()
{
    SOCKET s = 0;
    int rcv_size, snd_size, sz = sizeof(int);
    long ptr = 0, bytesRead = 0, p_size = MAX_BUF;

    char counter[10];
    char substring[MAX_STR];
    void *szPage, *recv_str, *send_str;

    int exp_count = 0;
    int ovector[VECT_SIZE];
    int matches = 0;

    int i = 0,j = 0;

    getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)&rcv_size, &sz);
    getsockopt(s, SOL_SOCKET, SO_SNDBUF, (char*)&snd_size, &sz);

[COLOR="Red"]   if ((recv_str = VirtualAlloc(NULL, rcv_size, MEM_COMMIT, PAGE_READWRITE)) == NULL)
        error(10);
    if ((send_str = VirtualAlloc(NULL, snd_size, MEM_COMMIT, PAGE_READWRITE)) == NULL)
        error(10);
    if ((szPage = VirtualAlloc(NULL, MAX_BUF, MEM_COMMIT, PAGE_READWRITE)) == NULL)
        error(10);[/COLOR]

    for (j=startCount; j<=stopCount; j++) {
        s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        connect(s, (SOCKADDR*)&sin_remote, sizeof(sin_remote));

        memset(send_str, 0, snd_size);
        strcpy(send_str, "GET http://");
        strcat(send_str, host);
        sprintf(counter, "%d", j);
        strcat(send_str, substr(query, "{ID}",counter));
        strcat(send_str, " HTTP/1.0\nHost: ");
        strcat(send_str, host);
        strcat(send_str, "\nUser-Agent: ");
        strcat(send_str, agent);
        strcat(send_str, "\nAccept: */*\nAccept-Language: en-US\n\n\n");
   
        send(s, send_str, (int)strlen(send_str)-1, 0);

        p_size = rcv_size;

        memset(szPage, 0, MAX_BUF);
        while (1) {
            memset(recv_str, 0, rcv_size);
            if ((bytesRead = recv(s, recv_str, rcv_size, 0)) <= 0)
                break;

            ptr += bytesRead;
            strcat(szPage, recv_str);
        }

        matches = pcre_exec(re, NULL, (char*)szPage, ptr, 0, 0, ovector, 300);

        for (i=0; i<=matches; i++) {
            memset(substring, 0, MAX_STR);
            pcre_copy_substring((char*)szPage, ovector, matches, i, substring, MAX_STR);
            fputs(substring, out);
        }

        closesocket(s);
    }

    VirtualFree(recv_str, rcv_size, MEM_DECOMMIT);
    VirtualFree(send_str, snd_size, MEM_DECOMMIT);
    VirtualFree(szPage, MAX_BUF, MEM_DECOMMIT);
}


вызов из программы:
Код:
printf("Trying to reserve memory...");
    if ((gmem = VirtualAlloc(NULL, MAX_ALLOC, MEM_RESERVE, PAGE_READWRITE)) == NULL) {
        puts("FAILED");
        error(-1);
    }
    puts("SUCCESS");

           _beginthread((void*)thread, 0, NULL);    // здесь вызываю поток (по рекомендациям авторитетных программистов использую _beginthread вместо API CreateThread()
    for (tid=0; tid<=100000; tid++) // здесь жду выполнения потока
    {
        printf(".");
    }

[COLOR=SeaGreen]//  thread(); если делаю вот так, то все работает, если выполняю код выше, память не выделяется[/COLOR]

    printf("Now trying to release memory...");
    if (!(VirtualFree(gmem, 0, MEM_RELEASE))) {
        puts("FAILED");
        error(-1);
    }
    puts("SUCCESS");
3
31 июля 2008 года
Green
4.8K / / 20.01.2000
Цитата: polaroid

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


Указатель в качестве буфера - это сильно!

Цитата: polaroid

а саму память выделяю с помощью VirtualAlloc().


Тоже из-за HTTP? А какая связь?

Цитата: polaroid

Память должна выделяться в каждом потоке, поэтому перед входом в поток (из основной программы) я вызываю VirtualAlloc() с флагом MEM_RESERVE (т.е. резервирую память, в моем слуае - 20мб), а в самих потоках уже VirtualAlloc с флагом MEM_COMMIT.


Советую почитать документацию (например MSDN) и задуматься:
Зачем вообще нужен MEM_RESERV и нафига он тебе?
Как работает MEM_COMMIT и нафига он тебе нужен отдельно от MEM_RESERV ?
Да и зачем тебе вообще нужен VirtualAlloc, чем malloc не угодил?

Цитата: polaroid

Все бы хорошо, но! Когда я создаю поток, VirutalAlloc возвращает NULL!


А код ошибки посмотреть рука не поднимается?

Цитата: polaroid

Подскажите, как решить проблему.


Используй malloc, пока не научишься пользоваться VirtualAlloc.

Цитата: polaroid

Я так подозреваю, что это возможно особенность архитектуры Windows,


VirtualAlloc - это функция Win32 API.

Цитата: polaroid

но материалов про это в сети не нашел.


MSDN
FirsSteps.ru
и ещё сотни сайтов, доступных по ссылке:
http://www.google.ru/search?complete=1&hl=ru&newwindow=1&rlz=1B3GGGL_ruRU213RU213&q=VirtualAlloc&btnG=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA&lr=&aq=-1&oq=

7.2K
31 июля 2008 года
polaroid
94 / / 05.07.2008
Указатель в качестве буфера - сказал не то, что имел ввиду(признаю свою вину, меру, степень, глубину:D), но я думаю смысл понятен. Про протокол Http имелось ввиду то, что ответное сообщение может прийти любого размера (заранее неизвестного, т.к. поле Content-Length посылают не все сервера, по крайней мере что касается реализации HTTP 1.0), поэтому нужно использовать динамическую память. Про malloc() - использовал, но в качестве нагрузки получил кучу неизвестных глюков (вероятно реализация функции кривовата). Так что malloc не подходит. А про MEM_RESERVE - http://www.developing.ru/com/virtual_memory_04.html, руководствуясь этой статьей я и реализовал работу с памятью. Уж не знаю что тут такого противоестественного (MEM_COMMIT и MEM_RESERVE).
3
31 июля 2008 года
Green
4.8K / / 20.01.2000
Цитата: polaroid
Про malloc() - использовал, но в качестве нагрузки получил кучу неизвестных глюков (вероятно реализация функции кривовата).


Зачем сразу грешить на функцию? Может, ты её просто готовить не умеешь?
Какие были глюки?

Цитата: polaroid

Так что malloc не подходит. А про MEM_RESERVE - http://www.developing.ru/com/virtual_memory_04.html, руководствуясь этой статьей я и реализовал работу с памятью. Уж не знаю что тут такого противоестественного (MEM_COMMIT MEM_RESERVE).


Подумай, как по-твоему вызов с MEM_COMMIT "поймет", что ты имеешь в виду ту область, которую зарезервировал с помощью MEM_RESERVE ?

И непонятно, зачем ты сначала резервируешь память, а потом её коммитишь отдельно в каждом потоке? А если учесть что несколько потоков захотят использовать твою зарезервированную память, как они её будут делить?

Почему бы, если уж не хочешь использовать malloc, не использовать в потоках MEM_RESERVE|MEM_COMMIT ?

7.2K
31 июля 2008 года
polaroid
94 / / 05.07.2008
При malloc() буфер мог просто не заполнятся. Иногда происходили вылеты. При дебаге увидел, что вылетает при выходе из функции (malloc), при восстановлении регистра esi (pop esi). Так что хз. GetLastError от VirtualAlloc() возвращает 8. Ищу пока описание ошибок, и пробую сделать MEM_RESERVE|MEM_COMMIT отдельно в потоке
11
31 июля 2008 года
oxotnik333
2.9K / / 03.08.2007
а не легче ли выделить небольшой буфер (1-4 кб, char buff[1024]) и в цикле опрашивать в этот буфер сокет, а из буфера помещать в какой нидь контейнер (list, vector)
здесь 3-й вариант
7.2K
31 июля 2008 года
polaroid
94 / / 05.07.2008
Наверное легче, только у меня консольное приложение. Да и язык C. В нем нет понятия классов
11
31 июля 2008 года
oxotnik333
2.9K / / 03.08.2007
Цитата: polaroid
Наверное легче, только у меня консольное приложение. Да и язык C. В нем нет понятия классов



Ну в файл пиши этот промежуточный буфер
ЗЫ: при чем тут консольное или нет?

5
31 июля 2008 года
hardcase
4.5K / / 09.08.2005
А как обстоит дело с синхронизацией? Подобные "вылеты" очень напоминают ошибки одновременного доступа к разделяемой памяти.
Может быть у вас malloc не является потокобезопасной и имеет смысл вокруг не семафор развернуть?
7.2K
31 июля 2008 года
polaroid
94 / / 05.07.2008
С синхронизацией пока никак, т.к. я пытаюсь запустить хотя-бы один поток. Проверил - ни malloc() ни VirtualAlloc() в потоке не работают.

Цитата:
Ну в файл пиши этот промежуточный буфер
ЗЫ: при чем тут консольное или нет?



Думал об этом, да это выход, но не решение проблемы :) Ведь браузеры как то загружают страницы переменной длины?

11
31 июля 2008 года
oxotnik333
2.9K / / 03.08.2007
а им есть куда грузить, у них свой динамический буфер реализован...
причем они не выделяют сразу по 20 метров под него
398
31 июля 2008 года
Alexandoros
630 / / 21.10.2005
Почему void thread(), когда написано в документации void( *start_address )( void * ) ????? Куда параметр дел? Память говориш глючит? Сфига б ей не глючить?

Код:
DWORD __stdcall thread(PVOID xxx)
{

...
    if ((recv_str = ()char *malloc(rcv_size)) == NULL)
        error(10);
...

free( recv_str );


}


int main()
{
...
HANDLE thr;
thr = CreateThread(NULL, 0, thread, NULL, 0, NULL);  
WaitForSingleObject(thr, 40000);

...
}
7.2K
31 июля 2008 года
polaroid
94 / / 05.07.2008
Что-то все-равно не помогло. Хоть с параметром, хоть без
14
31 июля 2008 года
Phodopus
3.3K / / 19.06.2008
rcv_size, snd_size какие значения принимают?

error 8 = ERROR_NOT_ENOUGH_MEMORY, так что доMEM_RESERVEился
убери вообще MEM_RESERVE и RELEASE из основной функции, поставь MEM_RELEASE вместо DECOMMIT в поток. Попробуй
7.2K
31 июля 2008 года
polaroid
94 / / 05.07.2008
Ох, ох, ох! Phodopus, сенкс, бро. Ошибка была в том, что я использовал getsockopt до создания сокета, и rcv_size, snd_size принимали огромные значения. Теперь все работает. Всем спасибо.
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог