Модераторы: feodorv, GremlinProg, xvr, Fixin
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> InterlockedIncrement vs CriticalSection 
:(
    Опции темы
Alca
Дата 29.9.2008, 16:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3993
Регистрация: 14.6.2006

Репутация: 5
Всего: 50



InterlockedIncrement vs. CriticalSection. 
Что лучше использовать с точки зрения производительности? И почему?


--------------------
PM WWW ICQ Skype Jabber   Вверх
GremlinProg
Дата 29.9.2008, 19:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



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

что из этого следует?

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

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

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

Если речь об атомарном управлении синхронными счетчиками (x++, x--, x+=y, x-=y, и т.п.), то смысла заводить крит секции тут нет, поскольку время выполнения этих операций намного меньше, чем время управления секцией, поэтому прямые вызовы InterlockedIncrement, InterlockedDecrement, InterlockedExchangeAdd, и т.п. будут наиболее эффективны.


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
Lazin
Дата 29.9.2008, 23:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 8
Всего: 154



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

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

MyObject* pointer = 0;

void initialize_pointer()
{
    if (pointer == 0)
        pointer = new MyObject();
}

в случае если ф-я  initialize_pointer будет вызваться более чем одним потоком может произойти утечка памяти, например поток А проверит указатель pointer, обнаружит что он равен 0, в это время его прервет планировщик задач, и передаст управление потоку Б, который то-же проверит указатель и обнаружит что он равен нулю, потом поток Б создаст объект MyObject и запишет его адрес в указатель, потом планировщик опять передаст управление потоку А, который уже "знает" что указатель равен нулю и поэтому он тоже создаст объект и перезапишет адрес хранящийся в указателе адресом нового объекта. В результате объект будет проинициализирован дважды, это частный случай такого бага как race condinion. Что-бы этого избежать нужно защитить этот участок кода критической секцией
Код

CRITICAL_SECTION pointer_lock;
MyObject* pointer = 0;

//где-то в коде
InitializeCriticalSection(&pointer_lock);

******
void initialize_pointer()
{
    EnterCriticalSection(&pointer_lock);
    if (pointer == 0)
        pointer = new MyObject();
    LeaveCriticalSection(&pointer_lock);
}

у этого кода есть 2 слабых места(и вообще он ужасен), первое - безопастность с точки зрения исключений, если конструктор MyObject бросит исключение, функция LeaveCriticalSection не будет вызвана, а это приведет к взаимной блокировке (deadlock) при следующем вызове initialize_pointer, эта проблема легко решается с помощью идиомы RAII. второе - время жизни указателя и критической секции не совпадают, если вызвать initialize_pointer до вызова InitializeCriticalSection то произойдет ошибка времени выполнения, эта проблема легко решается заворачиванием критической секции в класс и ее инициализацией в конструкторе класса, впрочем я отвлекся...

У атомарных операций другое предназначение, с их помощью к примеру можно состряпать свой примитив синхронизации, но нельзя ничего синхронизировать. Если два потока используют общую переменную, то, если один из потоков просто ее изменит, другой поток (если он выполняется на другом процессоре со своим кэшем) этого не увидит, плюс, компилятор может оптимизировать код и значение переменной не будет меняться так как вы того ожидаете. Что-бы этого не происходило, нужно использовать Interlocked функции, если переменная была изменена этой функцией, то другой поток, на другом процессоре сразу сможет ее прочитать, так-же это изменение будет атомарным.
Вот пример использования функции InterlockedCompareExchangePointer, эта функция сравнивает 2 указателя и если они равны, записывает в один из них значение третьего указателя. Это можно использовать например для создания очереди, операции push и pop которой можно выполнять параллельно из нескольких потоков, я же покажу как сделать буфер, в который можно записывать данные из нескольких потоков параллельно. Буфер будет состоять из массива buffer (lol) и указателя на конец использованой части буфера end. Что-бы записать что-нибудь в буфер, поток сначала должен изменить указатель end, увеличить его значение, а потом записать туда данные (начиная со старого значения end и заканчивая новым) 
Код

const size_t buffer_size = 0x1000000;
char buffer[buffer_size];//shared buffer

char* end = buffer;
bool write_to_buffer(char *data, size_t data_size)
{
restart:
    //сохраняем значения переменной end в локальную переменную
    //в случае если другой поток ее изменит, у нас останется старое значение
    char* old_end = end;
    //вычисляем новое значение для end
    char* new_end = old_end + data_size;
    //проверяем размер буфера
    if ( new_end < (buffer + buffer_size - 1) )
    {
        //выполняем операцию CAS для переменной end, если другой поток изменил значение переменной end, 
        //result не будет совпадать с old_end, а если result и old_end совпали, значит переменная end не менялась 
        PVOID result = InterlockedCompareExchangePointer((PVOID*)&end, (PVOID)new_end, (PVOID)old_end);
        if (result != (PVOID)old_end)
        {
            goto restart;//если не совпало, значит между сохранением значения указателя end
            //и вызовом InterlockedCompareExchange, значение указателя end зменилось
            //и нужно все повторить сначала (busy wait)
        }
        memcpy((void*)old_end, (void*)data, data_size);//копирование данных в буфер
        return true;
    }
    return false;//буфер переполнен
}

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

Это сообщение отредактировал(а) Lazin - 30.9.2008, 08:33
PM MAIL Skype GTalk   Вверх
GremlinProg
Дата 30.9.2008, 08:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Цитата(Lazin @  30.9.2008,  01:20 Найти цитируемый пост)
Alca, как можно сравнивать интерлоки и крит. секции, первые нужны для выполнения атомарных операци, а вторые для синхронизации.

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


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
Lazin
Дата 30.9.2008, 08:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 8
Всего: 154



(upd)
написал небольшой тестик, оно работает smile 
Код

#include <iostream>
#include <fstream>
#include <boost/thread.hpp>
#include <boost/bind.hpp>


const size_t buffer_size = 0x1000000;
char buffer[buffer_size];//shared buffer

char* end = buffer;
bool write_to_buffer(char *data, size_t data_size)
{
restart:
    //сохраняем значения переменной end в локальную переменную
    //в случае если другой поток ее изменит, у нас останется старое значение
    char* old_end = end;
    //вычисляем новое значение для end
    char* new_end = old_end + data_size;
    //проверяем размер буфера
    if ( new_end < (buffer + buffer_size - 1) )
    {
        //выполняем операцию CAS для переменной end, если другой поток изменил значение переменной end, 
        //result не будет совпадать с old_end, а если result и old_end совпали, значит переменная end не менялась 
        PVOID result = InterlockedCompareExchangePointer((PVOID*)&end, (PVOID)new_end, (PVOID)old_end);
        if (result != (PVOID)old_end)
        {
            goto restart;//если не совпало, значит между сохранением значения указателя end
            //и вызовом InterlockedCompareExchange, значение указателя end зменилось
            //и нужно все повторить сначала (busy wait)
        }
        memcpy((void*)old_end, (void*)data, data_size);//копирование данных в буфер
        return true;
    }
    return false;//буфер переполнен
}


void thread_1()
{
    char *ptr = "first_thread\n";
    while (write_to_buffer(ptr, 13))
        ;
}

void thread_2()
{
    char* ptr = "second_thread\n";
    while (write_to_buffer(ptr, 14))
        ;
}

int main()
{
    ZeroMemory(buffer, buffer_size);
    boost::thread t1 (&thread_1);
    boost::thread t2 (&thread_2);
    t1.join();
    t2.join();
    std::fstream file ("test.result", std::ios::out);
    file << buffer << std::flush;
    file.close();
}

PM MAIL Skype GTalk   Вверх
Alca
Дата 30.9.2008, 11:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3993
Регистрация: 14.6.2006

Репутация: 5
Всего: 50



СИНХРОНИЗАЦИЯ
Синхронизация потоков в пользовательском режиме

Это сообщение отредактировал(а) Alca - 1.10.2008, 13:03


--------------------
PM WWW ICQ Skype Jabber   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Системное программирование и WinAPI"
Fixin
GremlinProg
xvr
feodorv
  • Большое количество информации и примеров с использованием функций WinAPI можно найти в MSDN
  • Описание сообщений, уведомлений и примеров с использованием компонент WinAPI (BUTTON, EDIT, STATIC, и т.п.), можно найти в MSDN Control Library
  • Непосредственно, перед созданием новой темы, проверьте заголовок и удостоверьтесь, что он отражает суть обсуждения.
  • После заполнения поля "Название темы", обратите внимание на наличие и содержание панели "А здесь смотрели?", возможно Ваш вопрос уже был решен.
  • Приводите часть кода, в которой предположительно находится проблема или ошибка.
  • Если указываете код, пользуйтесь тегами [code][/code], или их кнопочными аналогами.
  • Если вопрос решен, воспользуйтесь соответствующей ссылкой, расположенной напротив названия темы.
  • Один топик - один вопрос!
  • Перед тем как создать тему - прочтите это .

На данный раздел распространяются Правила форума и Правила раздела С++:Общие вопросы .


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Chipset, Step, Fixin, GremlinProg, xvr. feodorv.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Системное программирование и WinAPI | Следующая тема »


 




[ Время генерации скрипта: 0.0471 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.