Sleep
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 11.05.05 12:35
Оценка:
IT>Знаешь какая функция больше всего мешает нормальной работе многопоточных приложений? Правильно, Sleep. Ты думаешь она передаёт управление другому потоку и сохраняет тем самым процессорные тики. Но на самом деле она вмешивается в процесс переключения задач и заставляет систему по новой пересчитывать своё состояние.

Как быть? Что посоветуете?

(я как раз sleep-ов наляпал... )
Re: Sleep
От: Дарней Россия  
Дата: 11.05.05 14:07
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

IT>>Но на самом деле она вмешивается в процесс переключения задач и заставляет систему по новой пересчитывать своё состояние.


зачем — пересчитывать? %)
Насколько я понимаю, он должен просто передавать управлению следующему в очереди потоку. Вроде бы Рихтер против его применения не предостерегает
Всех излечит, исцелит
добрый Ctrl+Alt+Delete
Re: Sleep
От: IT Россия linq2db.com
Дата: 11.05.05 16:56
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

IT>>Знаешь какая функция больше всего мешает нормальной работе многопоточных приложений? Правильно, Sleep. Ты думаешь она передаёт управление другому потоку и сохраняет тем самым процессорные тики. Но на самом деле она вмешивается в процесс переключения задач и заставляет систему по новой пересчитывать своё состояние.


СГ>Как быть? Что посоветуете?


Один Sleep в одном потоке не сделает погоды. Речь идёт о серверных многопоточных приложениях, где количество потоков измеряется десятками и сотнями и слипы вызываются по делу и без дела. В таких приложениях ими лучше не злоупотреюлять. Т.е. когда много-много потоков и много-много слипов, то это плохо. Вообще же я привёл этот пример больше для того чтобы показать что благие намерения могут легко оказать медвежью услугу.
... << RSDN@Home 1.1.4 beta 5 rev. 395>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: Sleep
От: IT Россия linq2db.com
Дата: 11.05.05 17:07
Оценка:
Здравствуйте, Дарней, Вы писали:

Д>Насколько я понимаю, он должен просто передавать управлению следующему в очереди потоку.


Вопрос в том какому именно. У нас же не простая очередь в магазин, у нас есть ещё и приоритеты.
... << RSDN@Home 1.1.4 beta 5 rev. 395>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: Sleep
От: DimV Россия  
Дата: 11.05.05 18:00
Оценка: :)
Здравствуйте, IT, Вы писали:

IT>Вопрос в том какому именно. У нас же не простая очередь в магазин, у нас есть ещё и приоритеты.


Следующему в этой же очереди, если за это время не появилось потоков в очередях, соответсвующих большему приоритету.
Re[2]: Sleep
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 11.05.05 18:09
Оценка:
Здравствуйте, IT, Вы писали:


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


Имхо, приложения с сотнями нитей -- не самое лучшее решение. К сожалению, убедился на собственном опыте. Лучше оказывается десяток-другой независимых процессов (однопоточных или с небольшим количеством потоков), взаимодействующих через какой-либо IPC.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re: Sleep
От: Quintanar Россия  
Дата: 11.05.05 18:10
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

IT>>Знаешь какая функция больше всего мешает нормальной работе многопоточных приложений? Правильно, Sleep. Ты думаешь она передаёт управление другому потоку и сохраняет тем самым процессорные тики. Но на самом деле она вмешивается в процесс переключения задач и заставляет систему по новой пересчитывать своё состояние.


СГ>Как быть? Что посоветуете?


СГ>(я как раз sleep-ов наляпал... )


Да чего тут советовать. sleep потребляет столько-то тактов (достаточно много), частоту вызовов можно оценить, следовательно можно прикинуть на сколько это тормозит работу по сравнению с иными методами реализации многопоточности.
Re[3]: Sleep
От: IT Россия linq2db.com
Дата: 11.05.05 20:00
Оценка: +1
Здравствуйте, eao197, Вы писали:

E>Имхо, приложения с сотнями нитей -- не самое лучшее решение. К сожалению, убедился на собственном опыте.


Это понятно, операционка будет процентов на 30-40 загружена переключением задач. Но иногда бывает надо.
Вообще, любая синхронизация — это вмещательство куда не следует. Лучше проектировать систему с учётом того, чтобы синхронизировать было нечего или как можно меньше. Это ещё один аргумент в пользу стэйтлес систем. Нечего шарить — нечего синхронизировать.
... << RSDN@Home 1.1.4 beta 5 rev. 395>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: Sleep
От: Шахтер Интернет  
Дата: 12.05.05 02:51
Оценка: +1
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, Дарней, Вы писали:


Д>>Насколько я понимаю, он должен просто передавать управлению следующему в очереди потоку.


IT>Вопрос в том какому именно. У нас же не простая очередь в магазин, у нас есть ещё и приоритеты.


Очередь с приоритетами. В реальности, путешествие в ядро и обратно в виндах займет гораздо больше времени, чем затраты на манипулирования очередью потоков.
... << RSDN@Home 1.1.3 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[3]: Sleep
От: FR  
Дата: 12.05.05 07:46
Оценка:
Здравствуйте, eao197, Вы писали:

E>Имхо, приложения с сотнями нитей -- не самое лучшее решение. К сожалению, убедился на собственном опыте. Лучше оказывается десяток-другой независимых процессов (однопоточных или с небольшим количеством потоков), взаимодействующих через какой-либо IPC.


Ну есть же еще легкие нити (те же фиберы из WinAPI) у них время переключения мизерное, из недостатков кооперативная многозадачность и отсутствия ускорения на многопроцессорных ситсемах, зато можно без проблем создавать десятки и сотни тысяч потоков.
Re[4]: Sleep
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 12.05.05 07:58
Оценка: 1 (1)
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, eao197, Вы писали:


E>>Имхо, приложения с сотнями нитей -- не самое лучшее решение. К сожалению, убедился на собственном опыте.


IT>Это понятно, операционка будет процентов на 30-40 загружена переключением задач. Но иногда бывает надо.


<offtopic>
По моему мнению, затраты на синхронизацию это только видимая (причем не полностью) часть айсберга. Неполностью видимая потому, что в многопоточном приложении затраты на лишнюю синхронизацию могут появляться совсем не там, где ожидалось. Например, если говорить о C++, то значительно притормаживать может работа с хипом. Ведь если используется тривиальный (читай штатный) распределитель памяти, то обращения к new/delete выполняют незаметную для программиста синхронизацию. И если приложение активно использует хип (а у меня так чаще всего и бывает), то операции new/delete становятся более дорогими чем непродуманные sleep-ы или лишние ожидания на conditional variable.

Но что хуже всего с многопоточными приложениями -- это сопровождение и дополнительная адаптация уже запущенной системы. Если можество нитей запускаются только для того, чтобы на каждой из них выполнить какую-то конкретную операцию (например, синхронный вызов удаленной стороны по RPC), то особых проблем нет. А вот если в приложении есть несколько почти независимых активных объектов, каждый из которых выполняет конкретную функцию и использует набор своих подчиненных нитей, тогда со временем начинается головная боль. Например, есть некий сервер-маршрутизатор, который получает информацию с нескольких входов и передает ее (возможно преобразованную) на несколько выходов. Каждый вход и выход, а так же все необходимые прикладные объекты-маршрутизаторы обслуживаются своими групами нитей. Со временем оказывается, что конфигурацию какого-то из входов надо бы поправить. Но при этом нельзя останавливать весь процесс. Тогда нужно программировать каждую часть процесса так, чтобы она позволяла выполнять динамическую переконфигурацию. Еще хуже, когда нужно внести некоторые изменения в код. Скажем поменять DLL-ку используемую какой-то из частей приложения. Здесь без останова с последующим перезапуском вообще не обойтись.

В таких случаях лучше разбить приложение на несколько более простых процессов и организовать между ними какие-то IPC, чем пытаться впихнуть всю функциональность в один процесс. Хотя иногда кажется, что решение на нитях внутри одного процесса сделать проще, т.к. никакого IPC не нужно, все находится в памяти одного процесса и т.д. и т.п.
</offtopic>

IT>Вообще, любая синхронизация — это вмещательство куда не следует. Лучше проектировать систему с учётом того, чтобы синхронизировать было нечего или как можно меньше. Это ещё один аргумент в пользу стэйтлес систем. Нечего шарить — нечего синхронизировать.


Тогда, имхо, синхронизация будет на уровне IPC, когда один стэйтлес процесс будет передавать/получать данные от другого процесса.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[4]: Sleep
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 12.05.05 07:59
Оценка:
Здравствуйте, IT, Вы писали:

IT>Вообще, любая синхронизация — это вмещательство куда не следует. Лучше проектировать систему с учётом того, чтобы синхронизировать было нечего или как можно меньше. Это ещё один аргумент в пользу стэйтлес систем. Нечего шарить — нечего синхронизировать.


Да, синхронизация в main-stream операционках типа Windows/Linux очень дорого стоит (Aos Bluebottle — другое дело, там синхронизация дается даром).

Думал как сделать как можно меньше синхронизаций. Придумал следующее.
Допустим есть активности A и B которые должны передавать друг другу объекты. Тогда я завожу два буфера фиксированного размера:
      +--buffer AB--+
      |             |
   A--+             +--B
      |             | 
      +--buffer BA--+


В буфер AB может писать только A, а B может только читать. Причем запись делается так:
if(buffAB[i] == null) buffAB[i] = obj;

т.е. объект пишется только в пустое место. Читается стало быть, наоборот
if(buffAB[i] != null) 
{
  obj = buffAB[i];
  buffAB[i] = null;
}

Активность A выполняет какую-то свою работу, а когда вдруг испытвает потребность отправить obj активности B, то просто записывает это obj в пустую ячейку буфера AB.

Активность B выполняет какую-то свою работу и время от времени сканирует буфер AB на наличие в нем != null ячеек, списывает их к себе и обnullяет списанные ячейки.

В обратную сторону сообщения посылаются аналогично, но через другой буфер — BA.

Проблема возникает, когда надо передать очень много разных obj, а буфер-то фиксированного размера!!! Вот в этих местах я и вставил Sleep — если буфер полон, то поспать.

То же самое можно было сделать через
lock(this)
{
  while(buff.IsFull) System.Threading.Monitor.Wait(this);
  Put();
}

+ вызов System.Threading.Monitor.PulseAll(this); - при очистке буфера

но, опять же в Windows это работало бы медленее,
lock() — слишком переслишком тормозной (программа замедляется в 20 — 50 раз).
Re[5]: Sleep
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 12.05.05 08:13
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

СГ>Здравствуйте, IT, Вы писали:


IT>>Вообще, любая синхронизация — это вмещательство куда не следует. Лучше проектировать систему с учётом того, чтобы синхронизировать было нечего или как можно меньше. Это ещё один аргумент в пользу стэйтлес систем. Нечего шарить — нечего синхронизировать.


СГ>Да, синхронизация в main-stream операционках типа Windows/Linux очень дорого стоит (Aos Bluebottle — другое дело, там синхронизация дается даром).


Все имеет свою цену, имхо. Если бы в Windows/Linux была возможность сделать даровую синхронизацию -- можешь быть уверен -- она бы давно была сделана. Кстати, в Windows синхронизация на CriticalSection или InterlockedXXX на порядок (если не больше) дешевле, чем на WaitForSingle(Multiple)Object.

СГ>Проблема возникает, когда надо передать очень много разных obj, а буфер-то фиксированного размера!!! Вот в этих местах я и вставил Sleep — если буфер полон, то поспать.


Для таких задач в Unix-ах используются condition variable -- штука поумнее простого Sleep-а.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[5]: Sleep
От: Quintanar Россия  
Дата: 12.05.05 09:37
Оценка: 1 (1) +1
Этот метод реализован в библиотеке OCaml —
http://caml.inria.fr/pub/docs/manual-ocaml/libref/Event.html
Возможно, подобные разработки есть и для других языков.

Есть еще более высокоуровневые способы синхронизации подобного типа, я приводил ссылки в Декларативном Программировании в треде Join calculus.
Есть реализации для Java и C#. Для C# на MS research можно поискать информацию по C omega — расширению C# с поддержкой этих возможностей.
Re[5]: Sleep
От: faulx  
Дата: 12.05.05 10:05
Оценка:
Здравствуйте, eao197, Вы писали:


E>Но что хуже всего с многопоточными приложениями -- это сопровождение и дополнительная адаптация уже запущенной системы. Если можество нитей запускаются только для того, чтобы на каждой из них выполнить какую-то конкретную операцию (например, синхронный вызов удаленной стороны по RPC), то особых проблем нет. А вот если в приложении есть несколько почти независимых активных объектов, каждый из которых выполняет конкретную функцию и использует набор своих подчиненных нитей, тогда со временем начинается головная боль. Например, есть некий сервер-маршрутизатор, который получает информацию с нескольких входов и передает ее (возможно преобразованную) на несколько выходов. Каждый вход и выход, а так же все необходимые прикладные объекты-маршрутизаторы обслуживаются своими групами нитей. Со временем оказывается, что конфигурацию какого-то из входов надо бы поправить. Но при этом нельзя останавливать весь процесс. Тогда нужно программировать каждую часть процесса так, чтобы она позволяла выполнять динамическую переконфигурацию. Еще хуже, когда нужно внести некоторые изменения в код. Скажем поменять DLL-ку используемую какой-то из частей приложения. Здесь без останова с последующим перезапуском вообще не обойтись.


В Эрланге с этим справились. Там можно изменять на лету код, в котором на текущий момент находится управление.

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


В Эрланге так и сделано, там нет разделяемых данных, все построено на обмене сообщениями.
Re[6]: Sleep
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 12.05.05 10:11
Оценка:
Здравствуйте, faulx, Вы писали:

F>В Эрланге с этим справились. Там можно изменять на лету код, в котором на текущий момент находится управление.

F>В Эрланге так и сделано, там нет разделяемых данных, все построено на обмене сообщениями.

Рад за Эрланг. Серьезно.
Но пока я буду работать в области C++ (с возможным креном в Java или .Net), а там эти проблемы актуальны.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[4]: Sleep
От: Gaperton http://gaperton.livejournal.com
Дата: 12.05.05 10:36
Оценка:
Здравствуйте, FR, Вы писали:

FR>Здравствуйте, eao197, Вы писали:


E>>Имхо, приложения с сотнями нитей -- не самое лучшее решение. К сожалению, убедился на собственном опыте. Лучше оказывается десяток-другой независимых процессов (однопоточных или с небольшим количеством потоков), взаимодействующих через какой-либо IPC.


FR>Ну есть же еще легкие нити (те же фиберы из WinAPI) у них время переключения мизерное, из недостатков кооперативная многозадачность и отсутствия ускорения на многопроцессорных ситсемах, зато можно без проблем создавать десятки и сотни тысяч потоков.


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

А можно всего этого не писать, а пользоваться платформой Erlang/OTP для таких задач (будет лучше). Со вставками на С++ для критичных по скорости мест и Java для гуев. Или дождаться Comega или стабильного релиза OCaml с реализованным Joint Calculus (для сильных духом). Также, массовую параллельность обеспечивает система Oz. Вообще, это сейчас очень модно стало, даже название новое придумали — Concurrency Oriented Programming.
Re[5]: Sleep
От: Quintanar Россия  
Дата: 12.05.05 10:49
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Есть один нюанс . Придется написать свой планировщик задач, свои примитивы синхронизации, и (по уму-то если) свои обкладки вокруг асинхронного API ввода-вывода. Зато тогда будет щастье — вы сможете эффективно реализовать совершенно призвольную модель синхронизации. Кода немного, но все это способен сделать хорошо только достаточно квалифицированный спец (считай, кусок оперсистемы пишешь — очень прикольно ).


G> Вообще, это сейчас очень модно стало, даже название новое придумали — Concurrency Oriented Programming.


И понятно почему модно, потому что будущее за многопроцессорными системами. К сожалению, на нескольких процессорах (лучше даже на нескольких десятках) фиберы (или continuations к примеру) уже не помогут
Re[5]: Sleep
От: Sinclair Россия https://github.com/evilguest/
Дата: 12.05.05 11:55
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

СГ>Думал как сделать как можно меньше синхронизаций. Придумал следующее.

СГ>
СГ>if(buffAB[i] == null) buffAB[i] = obj;
СГ>

СГ>т.е. объект пишется только в пустое место. Читается стало быть, наоборот
СГ>
СГ>if(buffAB[i] != null) 
СГ>{
СГ>  obj = buffAB[i];
СГ>  buffAB[i] = null;
СГ>}
СГ>

СГ>Активность A выполняет какую-то свою работу, а когда вдруг испытвает потребность отправить obj активности B, то просто записывает это obj в пустую ячейку буфера AB.
Лучше сделать эту запись при помощи вызова WINAPI PostThreadMessage.
СГ>Активность B выполняет какую-то свою работу и время от времени сканирует буфер AB на наличие в нем != null ячеек, списывает их к себе и обnullяет списанные ячейки.
Это лучше делать при помощи WINAPI GetMessage/PeekMessage.
СГ>Проблема возникает, когда надо передать очень много разных obj, а буфер-то фиксированного размера!!! Вот в этих местах я и вставил Sleep — если буфер полон, то поспать.
При использовании WINAPI проблем не возникает.
СГ>но, опять же в Windows это работало бы медленее,
СГ>lock() — слишком переслишком тормозной (программа замедляется в 20 — 50 раз).
Проверь быстродействие через очередь сообщений.
... << RSDN@Home 1.1.4 beta 5 rev. 395>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[5]: Вопрос об атомарности
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 13.05.05 13:40
Оценка:
СГ>В буфер AB может писать только A, а B может только читать. Причем запись делается так:
СГ>
СГ>if(buffAB[i] == null) buffAB[i] = obj;


Тут случился спор об атомарности операции присвоения buffAB[i] = obj;

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

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

 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.