Upload
dima-dzuba
View
409
Download
2
Embed Size (px)
Citation preview
Основныепонятия• Мультипроцессирование - использованиенесколькихпроцессоровдляодновременноговыполнениязадач.
• Мультипрограммирование - одновременноевыполнениенесколькихзадачнаодномилинесколькихпроцессорах.
МультипроцессированиеМультипроцессированиебываетнесколькихвидов:
• когдаиспользуютсяразличныевидыоборудования,например,одновременнаяработацентральногопроцессораиграфическогоускорителявидеокарты;
• когдаорганизуетсяодновременнаяработаравноправныхустройств,выполняющихсходныезадачи.
Вовторомслучаебываетдваподхода:
• свыделениемуправляющегоиподчиненныхустройств(асимметричноемультипроцессирование)
• сиспользованиемполностьюравноправных(симметричноемультипроцессирование).
SMPShared Memory Processor илиSymmetric MultiProcessorВтакихкомпьютерахнесколькопроцессоровподключеныкобщейоперативнойпамятииимеюткнейравноправныйиконкурентныйдоступ.Помереувеличениячислапроцессоровпроизводительностьоперативнойпамятиикоммутаторов,связывающихпроцессорыспамятью,становитсякритическиважной.ОбычновSMPиспользуются2-8процессоров;режечислопроцессоровдостигаетдесятков.Взаимодействиеодновременновыполняющихсяпроцессовосуществляетсяпосредствомиспользованияобщейпамяти,ккоторойимеютравноправныйдоступвсепроцессоры
MPPMassively Parallel ProcessorsВMPPиспользуютнесколькооднопроцессорныхилиSMP-систем,объединяемыхспомощьюнекоторогокоммуникационногооборудованиявединуюсеть.Приэтомможетприменятьсякакспециализированнаявысокопроизводительнаясредапередачиданных,такиобычныесетевыесредства- типаEthernet.ВMPPсистемахоперативнаяпамятькаждогоузлаобычноизолированаотдругихузлов,идляобменаданнымитребуетсяспециальноорганизованнаяпересылкаданныхпосети.ДляMPPсистемкритическойстановитсясредапередачиданных;однаковслучаемалосвязанныхмеждусобойпроцессоввозможноодновременноеиспользованиебольшогочислапроцессоров.ЧислопроцессороввMPPсистемахможетизмерятьсясотнямиитысячами.
NUMANon-UniformMemoryAccessЯвляетсякомпромиссоммеждуSMPиMPPсистемами:оперативнаяпамятьявляетсяобщейиразделяемоймеждувсемипроцессорами,ноприэтомпамятьнеоднороднаповременидоступа.Каждыйпроцессорныйузелимеетнекоторыйобъемоперативнойпамяти,доступккоторойосуществляетсямаксимальнобыстро;длядоступакпамятидругогоузлапотребуетсязначительнобольшевремени.
Процессыипотоки• Программа(program)– этопоследовательностькоманд,реализующаяалгоритм
решениязадачи.
• Процесс(process)– этопрограмма(пользовательскаяилисистемная)входевыполнения.
• Всовременныхоперационныхсистемахпроцесспредставляетсобойобъект– структуруданных,содержащуюинформацию,необходимуюдлявыполненияпрограммы.Объект"Процесс"создаетсявмоментзапускапрограммы(например,пользовательдваждыщелкаетмышьюнаисполняемомфайле)иуничтожаетсяпризавершениипрограммы.
• Процессможетсодержатьодинилинесколькопотоков(thread)– объектов,которымоперационнаясистемапредоставляетпроцессорноевремя.Сампосебепроцессневыполняется– выполняютсяегопотоки.Такимобразом,машинныекоманды,записанныевисполняемомфайле,выполняютсянапроцессоревсоставепотока.Еслипотоковнесколько,онимогутвыполнятьсяодновременно.
Мультипрограммирование• Вмультипрограммированииключевымместом
являетсяспособсоставлениярасписания,покоторомуосуществляетсяпереключениемеждузадачами(планирование),атакжемеханизм,осуществляющийэтипереключения.
• Повременипланированияможновыделитьстатическоеидинамическоесоставлениерасписания.• Пристатическомпланированиирасписаниесоставляется
заранее,дозапускаприложений,иоперационнаясистемавдальнейшемпростовыполняетсоставленноерасписание.
• Вслучаединамическогопланированияпорядокзапусказадачипередачиуправлениязадачамопределяетсянепосредственнововремяисполнения.
МультипроцессированиеvsМультипрограммирование
Нескольковычислительныхядерпроцессорапозволяютвыполнятьнесколькозадачодновременно.
Одноядропроцессораможетвыполнятьнесколькозадач,толькопереключаясьмеждуними.
Двавидауправлениямногозадачностью• вслучаеневытесняющей
многозадачностирешениеопереключениипринимаетвыполняемаявданныймоментзадача
• вслучаевытесняющеймногозадачноститакоерешениепринимаетсяоперационнойсистемой(илиинымарбитром),независимоотработыактивнойвданныймоментзадачи.
Планировщикзадач• Вытесняющаямногозадачностьпредполагаетналичиенекоторогоарбитра,принадлежащего
обычнооперационнойсистеме,которыйпринимаетрешениеовытеснениитекущейвыполняемойзадачикакой-либодругой,готовойквыполнению,асинхронносработойтекущейзадачи.
• Вкачественекоторогообобщенияможновыделитьпонятие"моментперепланирования",когдаактивируетсяпланировщикзадачипринимаетрешениеотом,какуюименнозадачувследующиймоментвременинадоначатьвыполнять.Принципы,покоторымназначаютсямоментыперепланирования,икритерии,покоторымосуществляетсявыборзадачи,определяютспособреализациимногозадачностииегосильныеислабыестороны.
• Перепланирование:• Еслисменаприоритетавызываетперепланирование - значит,этосистемасабсолютными
приоритетами ивытесняющеймногозадачностью.• Еслимоментыперепланированиянаступаютпоисчерпаниювременных квантов(возможно
постоянногоразмера, авозможноипеременного), тосистемаподдерживает вытесняющуюмногозадачность сквантованием.
Причиныизмененияприоритета• запускзадачи(длявозможноскорейшегоначалаисполнения);
• досрочноеосвобождениепроцессорадоисчерпанияотведенногокванта(великшанс,чтозадачаивэтотразтакжебыстроотдаступравление);
• частыйвызовоперацийввода-вывода(приэтомзадачачащенаходитсявожиданиизавершенияоперации,нежелизанимаетпроцессорноевремя);
• продолжительноеожиданиевочереди(приоритетожидающейзадачичастопостепенноначинаютувеличивать);
Невытесняющая (кооперативная)многозадачностьПланировщик(scheduler) неможетзабратьвремяувычислительногопотока,покатотсамегонеотдаст.
Вытесняющийпланировщик• Унасестьдвавычислительныхпотока,выполняющих
однуитужепрограмму,иестьпланировщик,которыйвпроизвольныймоментвременипередвыполнениемлюбойинструкцииможетпрерватьактивныйпотокиактивироватьдругой.
• instruction pointer иstack pointer—хранятсяврегистрахпроцессора.Кроменихдлякорректнойработынеобходимаидругаяинформация,содержащаясяврегистрах:флагисостояния,различныерегистрыобщегоназначения,вкоторыхсодержатсявременныепеременные,итакдалее.Всеэтоназываетсяконтекстомпроцессора.
ПереключениеконтекстаПереключениеконтекста— заменаконтекстаодногопотокадругим.Планировщиксохраняеттекущийконтекстизагружаетврегистрыпроцессорадругой.
Текущаяпрограммапрерываетсяпроцессоромврезультатереакциинавнешнеесобытие— аппаратноепрерывание— ипередаетуправлениепланировщику
Зачемприменятьмногозадачность?1. Разделениепрограммынанезависимыечасти.Одинпроцессвыполняетоднузадачу
(например,взаимодействиеспользователем),адругой– другую(например,вычисления).
2. Дляувеличенияпроизводительности.
Увеличениечислапараллельныхпроцессовневсегдаприводиткускорениюпрограммы.
HelloWorld!Example99_Thread1. #include <iostream>2. #include <thread>3. void hello() {4. std::cout<<"Hello Concurrent World\n";5. }
6. int main(int argc,char * argv[]){7. std::thread t(hello); // launch new thread8. t.join(); //wait for finish of threads
9. cin.get();10.return 0;11.}
std::threadстандартС++11Конструктор
template <class Fn, class... Args> explicit thread (Fn&& fn, Args&&... args);
Принимаетвкачествеаргументафункториегопараметры.
void join();
Ожидаеткогдавыполнениепотоказакончится.
Какпередатьобъект?Example100_ThreadFunctor1. #include <thread>2. #include <iostream>3. class MyClass{4. public:5. // thread вызовет переопределенный оператор6. void operator()(const char* param){7. std::cout << param << std::endl;8. }9. };10. int main() {11. // передается копия объекта!12. std::thread my_thread(MyClass(),"Hello world!");13. my_thread.join();14. return 0;15. }
ПередачапараметроввпотокExample102_ThreadMoveSemanticsСпомощьюсемантикиперемещенияможнопередаватьданныевпоток,избегаянакладныхрасходовнакопированиебольшогообъемаданныхвстекпотока.
Ссемантикойперемещениямы100%уверенычтокданнымнашегопотоканебудетдоступаиздругихпотоков.
class MyClass {
protected:
std::unique_ptr<Param> ptr;
public:
MyClass( std::unique_ptr<Param> &¶m) {
ptr = std::move(param);}
MyClass(MyClass &&other) { // конструктор перемещения
ptr = std::move(other.ptr);}
MyClass(const MyClass &) = delete;
MyClass & operator=(const MyClass &)= delete;
void operator()() { } // тут код потока
};
Полезныефункцииstd::this_threadstd::thread::hardware_concurrency()
ВозвращаетколичествоThreadкоторыемогутвыполнятьсяпараллельнодляданногоприложения.
std::this_thread::get_id()
Возвращаетидентификаторпотока.
std::this_thread::sleep_for(std::chrono::milliseconds)
Позволяетусыпитьпотокнавремя
Какдождатьсязавершенияпотокакрасиво?Example104_ScopedThreadclass Scoped_Thread {std::thread t;public:Scoped_Thread(std::thread&& t_) : t(std::move(t_)) {if (!t.joinable()) throw std::logic_error("No thread");
};Scoped_Thread(std::thread& t_) : t(std::move(t_)) {if (!t.joinable()) throw std::logic_error("No thread");
};Scoped_Thread(Scoped_Thread & other) : t(std::move(other.t)) { };Scoped_Thread(Scoped_Thread && other) : t(std::move(other.t)) {};Scoped_Thread& operator=(Scoped_Thread &&other) {t = std::move(other.t); return *this;}
~Scoped_Thread() {if (t.joinable()) t.join();};
};
Проблемыработысдинамическими структурамиданныхвмногопоточнойсреде
Приудаленииэлементаизсвязанногоспискапроизводитсянесколькоопераций:
- удалениесвязиспредыдущимэлементом
- удалениесвязисоследующимэлементом
- удалениесамогоэлементасписка
Вовремявыполненияэтихоперацийкэтимиэлементамиобращатьсяиздругихпотоковнельзя!
ПотоковаябезопасностьСвойствокода,предполагающееегокорректноефункционированиеприодновременномисполнениинесколькимипотоками.
Основныеметодыдостижения:
• Реентрабельность;
• Локальноехранилищепотока;
• Взаимноеисключение;
• Атомарныеоперации;
РеентерабельностьСвойствофункции,предполагающеееёкорректноеисполнениевовремяповторноговызова(например,рекурсивного).
• Недолжнаработатьсостатическими(глобальнымиданными);
• Недолжнавозвращатьадресстатических(глобальныхданных);
• Должнаработатьтолькосданными,переданнымивызывающейстороной;
• Недолжнамодифицироватьсвоегокода;
• Недолжнавызыватьнереентерабельныхпрограммипроцедур.
Потоконебезопасный кодExample105_RaceConditionvoid add_function( long * number){for( long i=0;i<1000000000L;i++) (*number)++;}
void subst_function( long * number){for( long i=0;i<1000000000L;i++) (*number)--;}
int main() {long number = 0;{Scoped_Thread th1(std::move(std::thread(add_function,&number))); Scoped_Thread th2(std::move(std::thread(subst_function,&number)));}// Результат неопределен!std::cout << "Result:" << number << std::endl;return 0;}
ВзаимноеисключениеExample108_Mutex1Мьютекс—базовыйэлементсинхронизацииивС++11представленв4формахвзаголовочномфайле<mutex>:
mutexобеспечиваетбазовыефункцииlock()иunlock()инеблокируемыйметодtry_lock()
timed_mutexвотличиеотобычногомьютекса,имеетещедваметода:try_lock_for()иtry_lock_until()позволяющихконтролироватьвремяожиданиявхождениявmutex
РекурсивноевхождениеMutexExample109_Mutex21. recursive_mutex
можетвойти«самвсебя»,т.е.Поддерживаетмногократныйвызовlockизодногопотока.Содержитвсефункцииmutex.
2. recursive_timed_mutexэтокомбинацияtimed_mutex иrecursive_mutex
Замки
lock_guardExample110_LockGuard1. Захват в конструкторе2. Освобождение в деструкторе3. Используются методы мьютексов
◦ Захват:voidlock();◦ Освободить:voidunlock();
unique_guardExample111_UniqueLock1. Тоже,чтоlock_guard2. Используютсяметодымьютексов
◦ Попытатьсязахватить:booltry_lock();
◦ Захватсограничением:voidtimed_lock(...);
3. +Дополнительныефункцииполучениямьютекса,проверки«захваченности»...
ОпасностивмногопоточноесредеExample101_RaceConditionint array[100];
…
std::thread s1(sort, array, 100, less);
std::thread s2(sort, array, 100, more);
s1.join();
s2.join();
Когданесколькопотоковимеютдоступкоднимитемжеданным(бесконтрольный),торезультатработыалгоритманеопределен!
ЛокальноехранилищепотокаExample107_TLC• Наборстатических/глобальных переменных,локальныхпоотношениюк
данномупотоку.
• Отличиеотреентерабельностивтом,чтоиспользуютсянетолько«локальныепеременныепотока»иегопараметры,,ноиспециальныйменеджерресурсов.
Вообщеговоря,вданномпримереTLSнесовсемпотокобезопасный.
Потоко-безопасныйStackExample112_ThreadSafeStackКлассы«обертки»позволяютнепротиворечивоиспользоватьмьютекс вRAII-стилесавтоматическойблокировкойиразблокировкойврамкаходногоблока.Этиклассы:
lock_guardкогдаобъектсоздан,онпытаетсяполучитьмьютекс (вызываяlock()),акогдаобъектуничтожен,онавтоматическиосвобождаетмьютекс (вызываяunlock())
Печатьнаэкранэтотожеразделяемыйресурс– такчтопечатьтожезащищаеммьютексом.
DeadlockExample113_Deadlockstd::lock_guard<std::mutex> lock(a);
std::lock_guard<std::mutex> lock(b);
std::lock_guard<std::mutex> lock(b);
std::lock_guard<std::mutex> lock(a);
Возникаеткогданесколькопотоковпытаютсяполучитьдоступкнесколькимресурсамвразнойпоследовательности.
ПримеропаснойситуацииExample114_PassOutПередачаданныхиззаграницуlock.
Правило:
Никогданепередавайтевовнеуказателиилиссылкиназащищаемыеданные.Этокасаетсясохранениеданныхввидимойобластипамятиилипередачиданныхкакпараметравпользовательскуюфункцию.
ExceptionsвмногопоточнойсредеExample115_Exception
1. Исключениямеждупотокаминепередаются!2. Нужноустроитьхранилищеисключений,длятогочтобыихпотомобработать!
future+asyncExample117_Futurefuture– служитдляполучениярезультатавычисленийиздругогопотока.
Т.е.Функциявыполняемаяthread теперьможетвозвращатьзначение.
– этошаблон(параметр– типвозвращаемогозначения)
– конструируетсяспомощьюstd::async
— результатвыполненияполучаетсяметодомget()
— asyncзапускаетпотокисинхронизируетрезультатсвозвращаемымfuture
T function() {
return T();
}
int main (){
std::future<T> fut = std::async (function);
T x = fut.get();
}
futureработаетнаpromiseExample118_Promisetemplate<classT>promise– шаблонкоторыйсохраняетзначениетипаT,котороеможетбытьполученоспомощьюfutureget_future – запросfuture,связанногосозначениемвнутриpromiseset_value – устанавливаетзначение(ипередаетеговвызовget_future)set_exception – передачаисключения