Re: Тестовое задание ...
От: AlexGin Беларусь  
Дата: 12.06.15 21:00
Оценка: +1 -7
Здравствуйте, уважаемый Selavi!

Сложно сказать, что именно они считают как слабо.
Лично мне не понравилось, что члены классов никак не выделены:

class CThreadPoolX: public IFinishClientTask
{
...
bool terminated;
...
std::vector<WorkThreadPtr> threads;
std::thread thread;
};

в теле метода их можно запросто перепутать с локальной (автоматической) переменной.

Я бы дал названия членам класса — примерно так (венгерская нотация):

bool m_bTerminated;
std::vector<WorkThreadPtr> m_VectThreads;
std::thread m_thread;
Re[3]: Тестовое задание ...
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 13.06.15 13:12
Оценка: 6 (2) +4
Здравствуйте, Selavi, Вы писали:

S>1)

S>Да, я мог бы реализовать конструктор копирования в CTask и создавать копии заданий работы пула потоков (кстати, тоже можно придраться за лишние операции и расход памяти)
S>Да, я мог бы реализовать корректное хранение и удаление заданий вне пула потоков

Возможно, я неудачно сформулировал.

В рамках пула тебе не надо было реализовывать управление ЖЦ заданий вообще. В задании же ясно показан "чистый" указатель на CTask. На стороне пользователя при этом можно управлять объектами как угодно, хоть в стеке их размещать — это уже не твоё дело. Главное, что будет полная ясность с политикой владения: пул заданиями не владеет, это ответственность пользователя. А если пул принимает на вход shared_ptr, то стереотипичная интерпретация такова, что контроль ЖЦ возложен на этот указатель и никаких дополнительных копий хранить не требуется.

А у тебя получается, что ты и на входе берёшь shared_ptr, и ещё требуешь хранить его вовне. Зачем?!

S>Но есть же какое то разумное ограничение на время для тестового задания? Или мне надо было писать промышленную версию? Я просто не стал морочиться и кстати, написал это и в комменте к строчке и в описании к ТЗ. Почему то Вы на это не обратили внимания.


Это никак не соотносится с тем, что ты написал в пояснительной записке.

S>2) Что такого плохого в this->clientTaskFinisher = clientTaskFinisher?


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

S>3) Чем же лучше 2хфазная инициализацию/разрушение?


Проще контролировать вылетающие исключения.

S>4) Вам не нравится использование интерфейсов в С++? Раздражает constructor injection? Что не так?


Тем, что __interface — это не C++, а MSVC.

S>5) Да, с комментариями больной вопрос. Лично я придерживаюсь идеи дядюшки Боба о том, что комментарии засоряют код и нужно комментировать названиями методов и переменных. Считаю, что код в ТЗ вполне понятен и не требует лишней прозы.


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

S>6) Может и так, не буду спорить, хотя это снова мелочь.


Дружище, это не мелочи. Это то, что в сумме создаёт впечатление о твоём задании и главное — о твоём отношении к читателям твоего кода. А оно у тебя, мягко говоря, не очень.

S>Похоже на мелкие придирки, которые каждый может изобрести на свой вкус


S>Честно говоря, я ожидал, что будут замечания к алгоритму выборки клиентов, либо к реализации многопоточности в стиле "здесь можно было бы улучшить быстродействие"


В здравом уме и трезвой памяти это мало, кто будет делать. Тестовое задание — это самопрезентация, здесь "шоу" важнее, чем содержание.

Если кратко, то впечатление складывается такое:

— Задание реализовано неточно (CTask* vs. shared_ptr<CTask>);
— Работает странно;
— Оформлено небрежно.

Вывод?

S>А в итоге — все свелось к недовольству плохим стилем кода и отсутствием комментариев, хотя я написал аж целую сагу о том, как эта фигня работает.


А как правило в тестовых заданиях и не ставят задачу реализовать какой-то сложный алгоритм — о них можно дискутировать годами. Обычно хотят просто посмотреть на твои способности и как раз на те самые мелочи, которые ты презрительно отвергаешь.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re: Тестовое задание ...
От: Слава  
Дата: 13.06.15 20:06
Оценка: 8 (1) +1 -1 :)
Здравствуйте, Selavi, Вы писали:

S>...выполнено слабо

S>К сожалению, добиться обратной связи на тему "что именно слабо" не удалось.

К сожалению, в современном С++ я ничего не понимаю. Но, судя по комментариям, вы в задании не выполнили некие ритуалы, которые должны выполняться, но о которых не пишут в книгах, считая оные ритуалы самоочевидными. Также, в каждой конторе эти ритуалы слегка отличаются. То есть, от вас хотят видеть то же, что они хотели видеть в своих проектах. Это не означает, что в проектах у них это самое имеется, там может быть (и скорее всего есть) ужасная кодовая лапша. Однако же, со своей кодовой кодовой базой они сделать ничего не могут, зато могут требовать от соискателя соответствия неким идеалам, описанным только у них в голове.

Итого — лучше иметь возможность беседовать с интервьювером напрямую и постараться вести разговор самому, то есть — задавать тему. Любой человек может задать кучу вопросов, на которую не ответит любой собеседник. Постарайтесь донести до интервьювера мысль, что спрашивать надо о том, что человек знает, а не пытаться намеренно убедиться в его невежесте.
Re[6]: Тестовое задание ...
От: antropolog  
Дата: 17.06.15 12:50
Оценка: +1 -3
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Твой алгоритм не соответствует условию ТЗ:

EP>

S>>Не должно быть "повисших" клиентов, даже если клиентов больше, чем потоков.
S>>Т.е. поток должен обрабатывать разных клиентов по-очереди, а не одного до завершения всех его задач.


100% соответствие

EP>Возьми простой случай одного рабочего потока — никакого "разных клиентов по-очереди" у тебя нет.

возможно у нас разное понимание "по-очереди". В моём понимании по-очереди — значит в порядке очереди, т.е. в порядке добавления в очередь. А твоё какое?

P.S. Очевидно же что это условие "по-очереди, а не одного до завершения всех его задач" добавлено чтобы оградиться от простого, очевидного, но кривого дизайна, когда заводят по одной очереди на клиента и пул выбирает из этой очереди таски пока она не пуста.

P.P.S.
всё обсуждение в этом треде похоже на заседание дагестанской академии наук, не хочу никого задеть, но реально, кто из отписывающихся в треде прочёл хоть одну книгу по многопоточному программированию? Вопрос риторический, можно не отвечать.
Re[2]: Тестовое задание ...
От: watchyourinfo Аргентина  
Дата: 12.06.15 21:49
Оценка: +2 :)
A>а вышел какой-то франкенштейн, непонятно что делающий

isn't it what all production code is all about?
Re[4]: Тестовое задание ...
От: antropolog  
Дата: 13.06.15 12:16
Оценка: +2 -1
Здравствуйте, Selavi, Вы писали:


S>1) Может возникнуть ситуация, когда у одного потока в очереди есть одно или больше заданий, хотя в это время же соседний поток свободен

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

S>2) Будут возникать ситуации параллельного выполнения заданий от 1го клиента. Это нарушение условия ТЗ и чтобы его исправить понадобится большем, чем 20 строк кода.

нет не будут, клиент с одним id будет всегда попадать в один и тот же поток

S>3) Чтобы реализовать равномерное распределение серверного времени по клиентам в Вашем варианте придется написать еще 20+ строчек кода

где в т.з. что-то о равномерном распределении?

S>Вывод: Мсье торопится

малыш, ты бы лучше прочёл хоть одну бы книгу про многопоточное программирование
Re[4]: Тестовое задание ...
От: pestis  
Дата: 15.06.15 03:22
Оценка: +3
Здравствуйте, Selavi, Вы писали:

S>Вывод: Мсье торопится


Завалил-то тестовое задание ты, а не антрополог
Re[14]: Тестовое задание ...
От: Handie  
Дата: 15.06.15 11:28
Оценка: +3
S>1) Вы делаете вывод о целом на основе его небольшой части. Т.е. если кандидат в точности не последовал заданию (тестовому, Карл!!!), то он сразу же "неспособен" и у него "отсутствует".

Если не последовал — значит вместо решения задач заказчика человек начнет придумывать свои велосипеды. Делать ТЗ без извращений — это уже похвально и ценно.

S>2) Cпособность кандидата самостоятельно мыслить для вас является минусом. Главное, чтобы было "точно как в ТЗ". Одной из лучших практик написания кода является взаимодействие заказчика-технолога-программиста. В вашем варианте программисту вообще не нужно думать о задаче. Нужно лишь следовать тз. В итоге задача конечно будет решена, но вместо 3х голов будут использованы только 2, причем не самые компетентные.


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

S>3) Вы смешиваете тестовое задание и реальную разработку. В большинстве случае ТЗ пишут спустя рукава, поскольку никому не хочется тратить кучу времени и сил на людей, которые оценят ваш труд поверхностным взглядом за 5 мин и в большинстве случаев вынесут решение на основе своего настроения и личных предубеждений. Но Вы об этом не думаете, ставите двойку и кричите — "Следующий!". Знаете в чем проблема? В том, что слишком много голодных кадров и слишком легко их перебирать. Вероятно ситуация в ближайшие годы изменится.


ТЗ это визитная карточка. Ситуация, вероятно, только усугубится и связано это с деградацией образования и оттоком мозгов в другие страны
Re: Тестовое задание ...
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 13.06.15 10:57
Оценка: 16 (2)
Здравствуйте, Selavi, Вы писали:

S>...выполнено слабо

S>К сожалению, добиться обратной связи на тему "что именно слабо" не удалось.

Просмотрел поверхностно, возможно, чего-то не заметил.

Вот это — очень и очень плохо:

TaskPtr *task = new TaskPtr(new CTaskDer(sec));


Коль скоро у тебя API на shared_ptr, то можно было бы ожидать чего-то такого:

TaskPtr task;
task.reset(new CTaskDer(sec));
pool->AddTask(ccc - 48, task);


Но если так сделать, тестовый пример падает. Дальше потянулась ниточка, которую я полностью распутывать не стал, увидев, что задание преждевременно разрушается в методе CThreadPoolX::ProcessFreeThreads(). Увидь я такое у коллег, первой мыслью было бы: "Что они курили?!"

Получается, что ты не смог наладить управление жизненным циклом, поэтому, не смотря на то, что пул оперирует shared_ptr-ами, тебе приходится обязательно держать их где-то вовне пула. Иными словами, нужно городить какую-то внешнюю схему для контроля за заданиями, уже отданными пулу. Теоретически можно, но зачем тогда строить пул на shared_ptr? Оставил бы там обычные указатели.

То есть действительно, реализация слабая.

Потом, ты не точно следуешь заданию. Так, в задании написано:

bool CThreadPool::AddTask(int _clientId, CTask* _task);


Обрати внимание, какой здесь показан указатель на CTask.

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

this->clientTaskFinisher = clientTaskFinisher;


Ещё немного по реализации.

1) Лучше было бы сделать двухфазную инициализацию/разрушение: конструктор + init и shutdown + деструктор. Меня бы напрягло отсутствие такой "автоматической" реакции у интервьюируемого.

2) Ключевое слово __interface? C++11? O'rly?

3) Комментарии. Это вообще больной вопрос. Ребята, ну не надо издеваться над интервьюерами, а? Или пусть будет паритет: у одних код без комментариев, у других ответ без разъяснений.

4) Тестовый пример лучше бы оформить в виде автоматизированных тестов, где, например, проверялась бы работа пула с заданиями нулевой длительности, с заданиями, выбрасывающими исключения и т.п. Диалоговый режим теста — это не тест, это demo.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[13]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 16.06.15 14:11
Оценка: 2 (1) +1
Здравствуйте, Selavi, Вы писали:

S>>>Заметьте, что моей реализации, которую все обос*али вышеописанных проблем нет)

EP>>Там есть проблемы покруче, например:
EP>>* одно и то же задание может выполнится несколько раз, так как не учитываются spurious wakeup.
EP>>* выполнение не заданного задания (segfault).
EP>>* отсутствие join для CThreadPoolX, в результате std::terminate.
S>Да, Вы правы...вот черт( Наконец то хороший анализ!

А до этого что, плохие были? Люди потратили своё время, посмотрели код, даже если тебе обидно — постарайся сдерживать эмоции.

S>А не могли бы подробней про 2 и 3?


2. Рассмотри ситуацию когда рабочий поток создался, не получил ни одного задания, а потом разрушился. В этом случае поток проснётся и попробует выполнять this->task, который указывает в никуда.

3. Управляющий поток CThreadPoolX создаётся, но нигде ни join'ится, ни detach'ится. В таком случае деструктор std::thread сделает std::terminate.

Наверняка есть ещё подобные проблемы, но дальше не смотрел.
Re: Тестовое задание ...
От: tlp  
Дата: 12.06.15 22:21
Оценка: +1 -1
Здравствуйте, Selavi, Вы писали:

S>...выполнено слабо

S>К сожалению, добиться обратной связи на тему "что именно слабо" не удалось.

S>Пожалуйста, покритикуйте реализацию.


1. комментариев нет от слова совсем

2.

int Size()
{
std::lock_guard<std::mutex> lock(this->mutex);

return container.size();
}

вместо int должен быть хотя бы size_t

3. TaskDer зачем-то засунут в Task.h

4. Смысл существования task.cpp непонятен, возникает вопрос понимает ли автор, что он делает. (хотя, наверное, это следствие п. 3)

5. использование голых указателей и C++0x в одном проекте. Трусы или крестик.

6.

CWorkThread::CWorkThread(IFinishClientTask *clientTaskFinisher)
: terminated(false)
, thread(&CWorkThread::ThreadFunc, this)
{
this->clientTaskFinisher = clientTaskFinisher;
}

непонятно зачем частично инициализируем обьект в init list, частично в теле конструктора.

7. Класс, в котором есть голые указатели и отсутствие для этого класса copy ctor и operator=

8. \r\n вместо std::endl

9. Библиотечные классы, выводящие debug-данные на консоль без возможности отключения.

10. "не должно быть подвисших клиентов даже если клиентов больше, чем потоков" — требование, которое невозможно соблюсти в текущей формулировке задания и API. Ваш код и комментарии не говорят об этом ни слова, вместо этого вы придумали эвристику, которая не работает.
Отредактировано 12.06.2015 22:22 tlp . Предыдущая версия .
Re[2]: Тестовое задание ...
От: Selavi  
Дата: 13.06.15 19:36
Оценка: +1 -1
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Тут никакой *_ptr вообще не нужен. Так обычно пишут Java/C# программисты не знающие идиом C++.

EP>Советую начать с чтения книжек Страуструпа.

И сколько же мне нужно прочитать книжек Страуструпа, чтобы найти идиому С++, которая объяснит почему тут не нужен умный указатель?

Наверное потому, что память все равно очистится при выходе из программы? Почему нельзя было просто это сказать, а не делать высокомерную отсылку к Страуструпу? Полезность такого поста нулевая.
Re[3]: Тестовое задание ...
От: Selavi  
Дата: 14.06.15 10:36
Оценка: -1 :)
Здравствуйте, Геннадий Васильев, Вы писали:

ГВ>Странная логика. И наниматель, и кандидат ищут друг в друге нечто более или менее определённое. Почему при этом именно работодатель должен старательно обходить сложные для кандидата моменты — сие неведомо. Простите, а кандидат тоже должен бояться спросить у работодателя, например, соблюдает ли тот КЗоТ? А то, вдруг не соблюдает и страшно оскорбится таким вопросом.


ГВ>И потом, то, что в этом топике сказали топикстартеру, это не какие-то специфические "ритуалы", доступные только посвящённым, а в основном банальные, уже давно устоявшиеся правила хорошего тона с небольшой поправкой на ситуацию собеседования. Естественно, все понимают, что в production-коде может царить хаос и кабакЪ, но это же не означает, что на собеседовании нужно заниматься тем же самым.



Ну мне кажется, что Вы преувеличиваете насчет банальных и давно устоявшихся.
Впрочем, наверное стоило бы говорить о требованиях к кандидату. Вы считате что человек, претендующий на 140к должен знать как минимум все что написано в этом топике? Имо, это все таки уровень сеньора и деньги другие. Если же Вы мне скажете, что это должен знать миддл, то я убьюсь об стенку.

Работая над этим ТЗ я потратил большую часть времени потратил на обдумывание алгоритма, который удовлетворяет требованиям задачи так, как я их вижу. На мой взгляд алгоритм получился очень даже неплохим и реализация его вполне себе рабочая, без дедлоков, гонок и прочего безобразия. Да, много деталей можно править и улучшать и в реальной обстановке я бы не раз возвращался к этому коду с целью оптимизации и рефакторинга, причем по собственному почину. А в итоге получается, что всем плевать на алгоритм, на ход размышлений, на то, что в итоге все работает, пусть и со скрипом. Главное, что я там вместо голого указателя использовал умный и что комменты не написал.

Кстати, неприятно, что в этой конторе сидят жлобы, которые даже не удосужились ответить. Подумаешь, кто-то там потратил N часов на ТЗ. Не понравилось, значит игнорим. Рашка, блин.
Отредактировано 14.06.2015 12:32 kaa.python . Предыдущая версия .
Re[6]: Тестовое задание ...
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 15.06.15 03:44
Оценка: +2
Здравствуйте, Олег К., Вы писали:

ОК>Такого безосновательного самомнения как у российских программистов я ни у кого из других национальностей не видел.


Точно-точно, я даже пару примеров могу привести:

Вот это первый
Автор: Олег К.
Дата: 15.06.15
:

Я его код не смотрел, но то что ты говоришь — это мелочи все.


А вот второй
Автор: Слава
Дата: 13.06.15
:

К сожалению, в современном С++ я ничего не понимаю. Но, судя по комментариям ...


Оснований, как ты понимаешь, у обоих высказавшихся недостаточно, но они этого нисколько не стесняются. Даже прямо говорят о том, что рассуждать будут безосновательно. Ну что ж... Честность — лучшая политика!
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[9]: Тестовое задание ...
От: Олег К.  
Дата: 15.06.15 05:39
Оценка: :))
ОК>>Я в свою очередь добавил, что некоторые из вещей — мелочи на который нормальный инженер не будет обращать внимания.

ГВ>Это уже просто праздник какой-то! То есть "нормальный инженер" не обращает внимание на отклонения реализации от письменного задания?


Если задание может быть непонято, то оно будет непонято! Поэтому во многих случаях явно обращают внимание читающего. Где в задании было написано, что отклоняться от интерфейса нельзя? Где другие детали описывающие какой должна быть реализация? Я привел выше несколько вопросов. А то задача сформулирована всего в пару строчек, так тут еще и юнит тесты от ТС желают!
Re[11]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 15.06.15 22:23
Оценка: 2 (1)
Здравствуйте, Selavi, Вы писали:

S>Заметьте, что моей реализации, которую все обос*али вышеописанных проблем нет)


Там есть проблемы покруче, например:
* одно и то же задание может выполнится несколько раз, так как не учитываются spurious wakeup.
* выполнение не заданного задания (segfault).
* отсутствие join для CThreadPoolX, в результате std::terminate.
Отредактировано 15.06.2015 22:59 Evgeny.Panasyuk . Предыдущая версия .
Re[17]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 16.06.15 19:46
Оценка: 2 (1)
Здравствуйте, Selavi, Вы писали:

EP>>Это не поможет, там остаётся возможность выполнения последнего задания дважды (даже не считая spurious wakeup).

S>Вот последняя версия рабочего метода потока. Разве тут есть вышеописанные проблемы?

this->task должен инициализироваться в конструкторе на nullptr (этого не было).
RunTask устанавливает this->task не под мьютексом — там, насколько я вижу, возможен data race при spurious wakeup:
void CWorkThread::RunTask
    this->task = task; // #1

void CWorkThread::ThreadFunc
        while (!this->task) // #2
              cv.wait(lock);


Далее, this->terminated — это обычный bool, соответственно тут тоже data race.
Re[19]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 16.06.15 21:08
Оценка: 2 (1)
Здравствуйте, Selavi, Вы писали:

EP>>RunTask устанавливает this->task не под мьютексом — там, насколько я вижу, возможен data race при spurious wakeup:

S>Не должно быть data race, поскольку в вызывающем коде перед RunTask выполняется проверка IsFree, которая в свою очередь, проверяет доступность мютекса, которым владеет рабочий поток в момент выполнения задания (пусть даже в результате spurious wakeup)
S>Хотя...если поток проснется между проверкой и вызовом RunTask, то будет печаль..

Об этом и речь.

S>Видимо лучше будет добавить в RunTask захват мютекса lockWorkFlag?


Это уберёт data race. Только надо делать правильный захват — на github'е сейчас неправильный.

EP>>Далее, this->terminated — это обычный bool, соответственно тут тоже data race.

S>Вот это я не понял. Как тут возможна гонка?

Чтения и записи в bool terminated никак не упорядоченны между потоками. Вот что говорит стандарт по этому поводу:

Two expression evaluations conflict if one of them modifies a memory location (1.7) and the other one accesses or modifies the same memory location.
...
The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior.

Re[3]: Тестовое задание ...
От: yetanotherplaceholder  
Дата: 13.06.15 13:37
Оценка: 1 (1)
Здравствуйте, Selavi, Вы писали:

S>3) Чем же лучше 2хфазная инициализацию/разрушение?


По опыту смартпойнтеры + отсутствие 2х-фазных инициализации/разрушения + многопоточность это почти как кислород + масло + Гомер Симпсон.

S>4) Вам не нравится использование интерфейсов в С++? Раздражает constructor injection? Что не так?


Тема __interface не раскрыта.
Re[14]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 16.06.15 14:33
Оценка: 1 (1)
Здравствуйте, GreenTea, Вы писали:

GT>>>Величина истории ограничена 100, если надо можно вынести это в параметр пула.

EP>>Требуется обойти всех клиентов по порядку + зацикливание на начало, всё
EP>>Зачем так сложно-то? В getNextClientTask создаётся два отображения, крутятся три цикла, всё это под мьютексом, да ещё и введён "параметр аппроксимации решения"
GT>Не понял. Какое еще зацикливание? Возможно сложно, но это первое что пришло в голову.

Есть три клиента. Их задания нужно обрабатывать циклически — 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3 ... — с поправкой на то, что задания одного клиента параллельно не выполняются.
Решение в лоб — просто создать список всех клиентов + общий циклический итератор.

GT>Хотелось бы посмотреть на ваше решение.


Например так:
* Для каждого клиента создаётся отдельная очередь заданий.
* Создаётся очередь из очередей всех клиентов.
* Требуется биективное отображение "клиент -> очередь". На add_task, если такой клиент уже был — то находим его очередь через отображение, и добавляем в неё, если же нет — добавляем клиента в отображение, ставим задание в очередь, а саму очередь добавляем (push) в конец очереди очередей клиентов. В случае если нужно добавить пачку заданий от одного клиента — то потребуется меньше синхронизации, так как не нужно каждый раз искать его очередь в отображении.
* Каждый рабочий поток делает в цикле try_pop из очереди очередей клиентов. Если получилось достать очередь клиента — то делаем try_pop задания из его очереди, если задание есть — то выполняем его, потом очередь клиента ставится (push) в конец общей очереди.
* Если потребуется, пустые (exhausted) очереди клиентов можно удалять, но тогда скорей всего lock free не получится, по крайней мере просто.
Re[2]: Тестовое задание ...
От: Selavi  
Дата: 13.06.15 06:07
Оценка: -1
Здравствуйте, AlexGin, Вы писали:

AG>Здравствуйте, уважаемый Selavi!


AG>Сложно сказать, что именно они считают как слабо.

AG>Лично мне не понравилось, что члены классов никак не выделены:

члены класса выделяются через this-> в местах, где есть вариант перепутать их с локальной переменной
Re[5]: Тестовое задание ...
От: Selavi  
Дата: 13.06.15 12:38
Оценка: +1
Здравствуйте, antropolog, Вы писали:

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



S>>1) Может возникнуть ситуация, когда у одного потока в очереди есть одно или больше заданий, хотя в это время же соседний поток свободен

A>да. может. но во-первых, если говорить о реальном мире, то это обычно самая оптимальная стратегия, а во-вторых, об оптимизации стратегии планировщика можно написать диссертацию, и это явно не для тестового задания

S>>2) Будут возникать ситуации параллельного выполнения заданий от 1го клиента. Это нарушение условия ТЗ и чтобы его исправить понадобится большем, чем 20 строк кода.

A>нет не будут, клиент с одним id будет всегда попадать в один и тот же поток

S>>3) Чтобы реализовать равномерное распределение серверного времени по клиентам в Вашем варианте придется написать еще 20+ строчек кода

A>где в т.з. что-то о равномерном распределении?

S>>Вывод: Мсье торопится

A>малыш, ты бы лучше прочёл хоть одну бы книгу про многопоточное программирование


Опять какая то демагогия, которую я могу бы разбить в 2 счета, но, судя по всему, мсье — обычный хам, поэтому говорить с ним не хочется.
Re[3]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 13.06.15 17:35
Оценка: +1
Здравствуйте, gandjustas, Вы писали:

G>Ты получаешь указатель, и не сообщаешь вызываемому коду когда задача завершена. Внимание вопрос: когда освобождать CTask?


При таком интерфейсе CTask сам может сообщить о своем завершении, видимо это и предполагается в исходной задаче.
Re: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 13.06.15 17:43
Оценка: +1
Здравствуйте, Selavi, Вы писали:

S>...выполнено слабо

S>К сожалению, добиться обратной связи на тему "что именно слабо" не удалось.
S>Пожалуйста, покритикуйте реализацию.

Буквально с первых строчек:
int _tmain(int argc, _TCHAR* argv[])
{
    std::unique_ptr<CThreadPoolX> pool (new CThreadPoolX(5));

Тут никакой *_ptr вообще не нужен. Так обычно пишут Java/C# программисты не знающие идиом C++.
Советую начать с чтения книжек Страуструпа.
Re[4]: Тестовое задание ...
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 14.06.15 12:10
Оценка: +1
Здравствуйте, Selavi, Вы писали:

S>Ну мне кажется, что Вы преувеличиваете насчет банальных и давно устоявшихся.


Может и преувеличиваю, но ИМХО, по отношению к C++ они сводятся к нескольким основным принципам:

— По возможности точно следовать заданию или переспросить, если что-то неясно (+ коммуникационные навыки);
— Отслеживать и обрабатывать ошибочные ситуации (+ уверенность, что ты знаешь как работают реальные программы);
— Хорошо оформить (+ умение следовать стилю оформления, + забота о читателях);
— Не тащить в коде ничего лишнего (+ к уверенности, что ты умеешь себя ограничивать в творческих порывах);
— Следовать устоявшимся идиоматическим приёмам (+ к уверенности, что кандидат понимает, как ими пользоваться);
— Сделать автоматические тесты хотя бы по граничным значениям (+ к знанию методов тестирования).

Это что-то вроде общего знаменателя, можно сделать больше, но не стоит делать меньше. Вроде бы, ничего запредельного.

На RSDN регулярно встречаются вопросы по тестовым заданиям, вот топик трёхлетней давности: http://rsdn.ru/forum/job/4788182
Автор: Геннадий Васильев
Дата: 21.06.12


Update: Вообще, конечно, надо ещё смотреть на саму задачу. Если задача простая, то определённо, хотят посмотреть на "второстепенные" моменты. Если сложная — то скорее на ход мысли и порядок тестирования. У тебя задача относительно простая, следовательно "ход мысли" интересует в последнюю очередь.

S>Впрочем, наверное стоило бы говорить о требованиях к кандидату. Вы считате что человек, претендующий на 140к должен знать как минимум все что написано в этом топике? Имо, это все таки уровень сеньора и деньги другие. Если же Вы мне скажете, что это должен знать миддл, то я убьюсь об стенку.


Помимо комментариев и других оформительских штучек, ИМХО, самые серьёзные претензии крутились вокруг двух вещей:

— Лишние абстракции;
— Необычное использование shared_ptr.

Вроде бы, ничего сложного даже для миддла.

Я не совсем согласен с gandjustas, что здесь стоит вводить lockfree-очередь: при прочих равных она бы не помешала, но мне кажется вишенкой на торте, без которой можно и обойтись.

S>Работая над этим ТЗ я потратил большую часть времени потратил на обдумывание алгоритма, который удовлетворяет требованиям задачи так, как я их вижу. На мой взгляд алгоритм получился очень даже неплохим и реализация его вполне себе рабочая, без дедлоков, гонок и прочего безобразия. Да, много деталей можно править и улучшать и в реальной обстановке я бы не раз возвращался к этому коду с целью оптимизации и рефакторинга, причем по собственному почину. А в итоге получается, что всем плевать на алгоритм, на ход размышлений, на то, что в итоге все работает, пусть и со скрипом. Главное, что я там вместо голого указателя использовал умный и что комменты не написал.


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

S>Кстати, неприятно, что в этой конторе сидят жлобы, которые даже не удосужились ответить. Подумаешь, кто-то там потратил N часов на ТЗ. Не понравилось, значит игнорим. Рашка, блин.


Согласен, если уж ты сам спрашивал о причинах непрохождения теста, могли бы и объяснить, хотя бы кратко. Высокомерно молчать — просто невежливо и уважения не добавляет.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Отредактировано 14.06.2015 12:23 Геннадий Васильев . Предыдущая версия .
Re[4]: Тестовое задание ...
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 14.06.15 13:00
Оценка: +1
Здравствуйте, Selavi, Вы писали:

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

Чуть со стула не упал когда прочитал. Какая разница как ты видишь требования. Человек который не может выяснить\понять что нужно заказчику и тратит большую часть времени на "обдумывание алгоритма" вообще не должен работать программистом.



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

А на мой взгляд ты вообще не решил исходную задачу. Там требовалось сделать вполне определенный API и оптимальную загрузку ресурсов. А у тебя лишние потоки и API не соответствует заданию. Ну и кому нужен твой алгоритм в итоге? Кроме того ты не уточнил задачу, возможно там требовался thread affinity для каждого клиента.

S>Да, много деталей можно править и улучшать и в реальной обстановке я бы не раз возвращался к этому коду с целью оптимизации и рефакторинга, причем по собственному почину. А в итоге получается, что всем плевать на алгоритм, на ход размышлений, на то, что в итоге все работает, пусть и со скрипом. Главное, что я там вместо голого указателя использовал умный и что комменты не написал.

Действительно всем плевать на алгоритм и на ход размышлений.
На что не плевать:
1) Понимание решаемой задачи
2) Точное выполнение задачи
3) Простота и понятность решения (в том числе комменты)
4) "чистый" код — без ненужных конструкций и в едином "хорошем" стиле
Ты всего этого не показал своим кодом, увы.

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

S>Кстати, неприятно, что в этой конторе сидят жлобы, которые даже не удосужились ответить. Подумаешь, кто-то там потратил N часов на ТЗ. Не понравилось, значит игнорим. Рашка, блин.

Ага, рашка во всем виновата и жлобы. А ты вообще на форум то зачем написал? Пожаловаться? Тогда зачем пример кода показывал? Жаловаться проще когда фактов нет.
Re[4]: Тестовое задание ...
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 14.06.15 16:41
Оценка: +1
Здравствуйте, Слава,

О каких, к дьяволу, ритуалах и тонкой стратегии собеседования идёт речь, если в задании прямо сказано:

bool CThreadPool::AddTask(int _clientId, CTask* _task);


А топикстартер пишет:

void AddTask(int clientID, const TaskPtr task);


Здесь три несоответствия на ровном месте:

— Не соответствует тип возвращаемого значения (void вместо bool);
— Изменён тип входного параметра (const TaskPtr вместо CTask*);
— Нарушен стиль оформления имён параметров (clientID вместо _clientId).

Что, для того, чтобы буквально выполнить задание и скопировать прототип функции нужно развозить, извини меня, сопли на километр? Специально планировать стратегию собеседования? Копаться в тоннах мануалов? Ещё что-то?
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[5]: Тестовое задание ...
От: Selavi  
Дата: 14.06.15 22:18
Оценка: :)
Здравствуйте, Геннадий Васильев, Вы писали:

ГВ>Здравствуйте, Слава,


ГВ>О каких, к дьяволу, ритуалах и тонкой стратегии собеседования идёт речь, если в задании прямо сказано:


ГВ>

bool CThreadPool::AddTask(int _clientId, CTask* _task);


ГВ>А топикстартер пишет:


ГВ>

void AddTask(int clientID, const TaskPtr task);


ГВ>Здесь три несоответствия на ровном месте:


ГВ>- Не соответствует тип возвращаемого значения (void вместо bool);

ГВ>- Изменён тип входного параметра (const TaskPtr вместо CTask*);
ГВ>- Нарушен стиль оформления имён параметров (clientID вместо _clientId).

ГВ>Что, для того, чтобы буквально выполнить задание и скопировать прототип функции нужно развозить, извини меня, сопли на километр? Специально планировать стратегию собеседования? Копаться в тоннах мануалов? Ещё что-то?


Ну хватит уже!

Придуман алгоритм и реализован корректно работающий пул потоков. Задача выполнена, пусть и не идеально.
Не нужно из простой невнимательности создавать химеру.
Re[6]: Тестовое задание ...
От: Олег К.  
Дата: 14.06.15 23:06
Оценка: +1
ОК>Я его код не смотрел, но то что ты говоришь — это мелочи все. Ну и большинство-таки не может даже нормально задачу поставить. На этом форуме мне всего лишь один х64 на ум приходит который может четко изложить, что ему надо. Это из того, что я сам тут видел.

ОК>Ну и задание само так себе. Ну и сам факт что они дают задание говорит, что чуваки просто не умеют поговорить нормально на интервью.


Добавлю. При желании можно и до фонарного столба докопаться. Вот простейший пример:

Пулу при инициализации задается число рабочих потоков:
bool CThreadPool::Init(int _numThreads);

Почему количество потоков не задается в конструкторе? Что должна вернуть функция? Что делать если нельзя создать нужное количество потоков? Убивать уже созданные или оставить их так как есть? Где описание всего этого? Почему от ТС требуются юнит-тесты а составители не желают четко описать чего хотят?

В каком состоянии должны быть потоки? Running или suspended? Где описание?

Что делает подчеркивание перед параметром? Обычно такая хрень — спереди или сзади — вводится для членов. Какая у них венгерская нотация для самих локальных переменных и членов?

Там еще много чего можно сказать о самом задании.
Re[6]: Тестовое задание ...
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 14.06.15 23:59
Оценка: +1
Здравствуйте, Selavi, Вы писали:

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


S>Сама постановка задачи предполагает множество трактовок. Не вижу смысла в трате времени для уточнения условий Тестового Задания, равно как и в переборе всех возможных вариантов, поэтому выбираю вариант, который мне нравится больше и реализую его. В данном случае — вариант с наиболее "долгождушим" клиентом.

И это no hire. Неважно что нравится тебе, важно что понравится заказчику. То что ты сделал — не понравилось. Тебе кучу причин уже привели почему оно могло не понравится.

S>И...вы вообще не обдумываете алгоритмы? Просто лепите первое что придет в голову? Честно, не понял замечания.

Нет, я тупо копирую готовые рабочие решения, а потом допиливаю детали.


S>Что для вас "оптимальная загрузка ресурсов"? Для меня в данном ТЗ — минимизация времени ожидания клиента.

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

S>И поверьте, что если это не так, то я без проблем реализую другой вариант.

Такая фраза не делает тебя специалистом. Кто мешал сразу уточнить детали и хотя бы API сделать ровно как написано?

S>А вот мне на текущем месте работы задачи ставят люди и эти нередко ошибаются или сами не знают чего хотят, поэтому я зачастую сам предлагаю вносить изменения в ТЗ. И не поверите — соглашаются и даже довольны.

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

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

S>Ну да. Хотел и сделал. И да, не оценили потому что такие как вы тут сидят не для того, чтобы оценивать, а для того, чтобы тешить свое эго. В этом топике 40+ ответов и из них только 3 действительно несут полезную нагрузку.
Собеседование не для того, чтобы оценивать твое творчество. Зачем жаловаться?

G>>Ага, рашка во всем виновата и жлобы. А ты вообще на форум то зачем написал? Пожаловаться? Тогда зачем пример кода показывал? Жаловаться проще когда фактов нет.

S>эммм...даже не знаю что на это ответить. Вы читали 1й пост?
Читал, поэтому и спрашиваю. Ты хотел показать себя, а не получить работу. Показал, но работодателю оказалось все равно. Зачем жаловаться?
Re[6]: Тестовое задание ...
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 15.06.15 04:51
Оценка: +1
Здравствуйте, Олег К., Вы писали:

ОК>Ну и задание само так себе.


Задание из разряда простейших и поставлено вполне внятно. Не нравится — не делай. На мой взгляд, в нём ничего нет такого, что вызывало бы острое неприятие.

ОК>Ну и сам факт что они дают задание говорит, что чуваки просто не умеют поговорить нормально на интервью.


Топикстартер поставил конкретный вопрос и получил на него вполне (как мне кажется) содержательные ответы. Если тебе хочется порассуждать об отвлечённом — никто не вправе тебе помешать.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[7]: Тестовое задание ...
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 15.06.15 06:12
Оценка: +1
Здравствуйте, Олег К., Вы писали:

ОК>Пулу при инициализации задается число рабочих потоков:

ОК>bool CThreadPool::Init(int _numThreads);

ОК>Почему количество потоков не задается в конструкторе?


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

ОК>Что должна вернуть функция?


Внезапно: true, если все в порядке и false, если не удалось создать все запланированные потоки.

ОК>Что делать если нельзя создать нужное количество потоков? Убивать уже созданные или оставить их так как есть? Где описание всего этого?


Ответы простейшие: а) вернуть false, б) убрать созданные.

ОК>Почему от ТС требуются юнит-тесты а составители не желают четко описать чего хотят?


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

ОК>В каком состоянии должны быть потоки? Running или suspended? Где описание?


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

ОК>Что делает подчеркивание перед параметром? Обычно такая хрень — спереди или сзади — вводится для членов. Какая у них венгерская нотация для самих локальных переменных и членов?


Стиль внешнего оформления задан, внутри пиши как хочешь или уточни у тех, кто дал задание.

ОК>Там еще много чего можно сказать о самом задании.


Угу, заметно.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[12]: Тестовое задание ...
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 15.06.15 06:56
Оценка: +1
Здравствуйте, Олег К., Вы писали:

ГВ>>

Ну и сам факт что они дают задание говорит, что чуваки просто не умеют поговорить нормально на интервью.

ГВ>>Я бы всё же, посоветовал тебе самостоятельно сравнить задачу и реализацию. Глядишь, мнение бы и поменялось.
ОК>Я верю вашим комментариям, но... Обрати внимание на "но." Мой посыл был в том, что интервьюеры могли бы установить на интервью подходит им кандидат или нет не напрягая кандидата своим заданием. Далее, он сделал задание, и пусть там будет самый худший код, но они даже не удосужились ответить ему. Что о них еще можно сказать?

Хотя бы не смешивай одно с другим. Я тоже согласен
Автор: Геннадий Васильев
Дата: 14.06.15
, что как минимум невежливо оставлять тесты без комментариев. Однако, я делаю из твоих пассажей такой вывод, что тебе просто хочется поругаться в адрес работодателей.

ГВ>>>>Естественно, не твой. Я привёл это высказывание как пример аналогичного подхода.

ОК>>>Этот чувак, в отличии от тебя, понимает, что интервью уже давно трансформировалось в экзамен. Ты этого не понимаешь.
ГВ>>Да, это отличное новое знание. Только какое отношение оно имеет к вопросу ТСа?
ОК>Да самое прямое. В отношении него была совершена несправедливость. Я объяснил причину этой несправедливости.

Вообще-то, ты ничего не объяснил, а всего лишь предложил возможное объяснение. Да и то, довольно примитивное, где всё сводится к "эго" — таких объяснений тебе накидает любой студент, только-только прочитавший Фрейда.

ГВ>>>>Пытаюсь показать тебе, что ты сам и демонстрируешь поведение, за которое упрекаешь других.

ОК>>>Это что же я демонстрирую? Наоборот, у меня нет эго и я могу поговорить с человеком без всяких заданий.
ГВ>>Прикольно. То есть попытка сократить время на оценку кандидата путём выдачи короткого задания — это признак гипертрофированного эго?
ОК>Э-э-э... Ты подумать над сказанным не хочешь? Кандидату приходится тратить свое время только потому что они не могут провести интервью! Им там (и тебе тоже) нужно пять минут на просмотр кода, но ведь кандидат-то больше времени потратил на его написание? Так что это получается игра в одни ворота. Типа "умные" чуваки будут решать подходит им кандидат или нет, но однако они лишили кандидата возможности увидеть их код.

Ну и? Да, будут. Причём тут эго-то? Люди смотрят, демонстрирует ли кандидат те навыки, которые от него ждут. Что тут такого оскорбительного? Всегда хочется посмотреть на работу мастера перед тем, как заключать с ним договор. Я понимаю, что общая ситуация может оказаться двусмысленной, как сложилась у ТС, но само по себе тестовое задание ничего оскорбительного в себе не несёт. ИМХО, надо иметь какую-то слишком уж ранимую натуру (читай, проблемы как раз с тем самым пресловутым эго), чтобы так остро на него реагировать.

ОК>>>А вот задающие такие задания (вообще любые а не конкретно это) считают себя более умными хотя более ни на что и не способны.

ГВ>>Ты всё ещё утверждаешь, что у тебя "нет эго"? Продолжай в том же духе, мне нравится твой стиль.
ОК>Ты заметь, в данной ветке только два человека сказали, что такой экзамен — ненормально. Остальные набросились на ТС, пусть он тысячу раз неправ.

Вот это твоя ошибка: набросились не на самого ТС, а на дело рук его. Это уже потом, когда ТС встал в позу, начали огрызаться. Ребята, учитесь отделять себя любимых от того, что вы делаете, это очень полезный навык.

ОК>Для этого, к сведению, и нужно интервью, чтобы обоим сторонам понять насколько они подходят друг другу. А тут кандидат пахал а те чуваки решили за пять минут (тебе ведь пять минут понадобилось, чтобы взглянуть на его код?) чтобы отсеять его.


Так и пяти минут достаточно, чтобы понять, что кандидат читает написанное с пятого на десятое. Я не знаю, кому как, но мне бы в ум не встало связываться с инженером, неспособным буквально последовать заданию. Какой смысл вести дальнейший разговор, если ключевой инженерный навык отсутствует?
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[4]: Тестовое задание ...
От: antropolog  
Дата: 17.06.15 08:01
Оценка: +1
Здравствуйте, VladFein, Вы писали:

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


A>>А надо было всего-лишь реализовать простой паттерн — привязку продюсера к конкретному потоку. В данном случае это делается отдельной очередью на каждый поток, и помещением таски в очередь с индексом = taskID % threadNum


VF>Такого требования нет в задании.

конечно нет, ведь я описываю имплементацию

VF>Таск от любого клиента может выполняться любым потоком, если только другой поток не выполняет в это время таск этого клиента.

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

VF>Мне видится простой алгоритм: все таски (с ID клиента) ставятся в одну очередь, в порядке поступления.

VF>CThreadPool имеет set "клиентов в процессе".
VF>Потокам при создании передаётся callback на:
VF>CTask* CThreadPool::NextTask(int& _clientId);
VF>Где [in, out] _clientId — при вызове равен ID предыдущего клиента (например, -1 для начала), а по возвращении — текущего.
VF>Потоки работают в бесконечном цикле NextTask() / Execute(). Ведь конечного условия не дано?

VF>CThreadPool::NextTask() удаляет ID предыдущего клиента из set'а, перебирает лист в поисках первого ID клиента, не найденного в том set'е, добавляет его в set, удаляет из очереди и возвращает потоку.

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

итого имеем:
set, queue, и один mutex, который защищает обе эти структуры и лочится на время работы:
NextTask,
AddTask
и всё это для того чтобы забалансить таски по тредам.

В то время как в моём случае:
отдельная lock-free очередь на каждый тред
NextTask/AddTask работает с конкретной очередью, мьютекс не нужен, поиск в очереди не нужен, добавление/удаление таски — одна CAS операция. Единственный недостаток — теоретическая разбалансировка, я повторюсь — теоретическая, например при малом количестве клиентов, и если у них внезапно совпал id % threadnum (ну или hash(id) % threadnum ). В реальном мире такая разбалансировка практически не встречается, а если встречается то на малом количестве "долгих клиентов" и при невезучем случае.

Иными словами — мой алгоритм оптимален для многоих клиентов с мелкими тасками ( т.к. минимальный shared state и отсутствие блокировок). Ваш алгоритм оптимален для "долгоиграющих" задач он небольшого количества клиентов (т.к. во-первых идеально балансирует, а во-вторых частота обращения к очереди задач относительно редка, что нивелирует влияние блокировок на перформанс ). Но повторюсь, об оптимальной балансировке в вакууме ( как и о трейде latency<->throughput ) можно написать диссертацию, а в тестовом задании ничего не сказано о балансировке, поэтому выбирается наиболее простой вариант ( и в то же время самый распространённый ), имплементация которого тривиальна.
Отредактировано 17.06.2015 8:08 antropolog . Предыдущая версия . Еще …
Отредактировано 17.06.2015 8:07 antropolog . Предыдущая версия .
Отредактировано 17.06.2015 8:04 antropolog . Предыдущая версия .
Отредактировано 17.06.2015 8:02 antropolog . Предыдущая версия .
Re[7]: Тестовое задание ...
От: Олег К.  
Дата: 18.06.15 04:19
Оценка: +1
ГВ>
ГВ>void CWorkThread::RunTask(int clientID, CTask* task)
ГВ>{
    this->>task = task;
    this->>clientID = clientID;

ГВ>    cv.notify_one();
ГВ>}
ГВ>


Непривычно но тем не менее все правильно.

ГВ>Мне показалось, что если бы формальные параметры были оформлены иначе, чем имена членов класса, код был бы чуть проще.


Обычно оформляют члены а не наоборот.
Тестовое задание ...
От: Selavi  
Дата: 12.06.15 19:32
Оценка:
...выполнено слабо
К сожалению, добиться обратной связи на тему "что именно слабо" не удалось.

Пожалуйста, покритикуйте реализацию.

Задание:

Написать пул потоков CThreadPool, обрабатывающий задачи, производные от класса CTask.

Задачи переопределяют функцию virtual void CTask::Execute() = 0;

Пулу при инициализации задается число рабочих потоков:
bool CThreadPool::Init(int _numThreads);

Задачи добавляются для разных клиентов. Задачи одного клиента обрабатываются в том порядке, в котором они были добавлены. Разные клиенты обслуживаются параллельно.
bool CThreadPool::AddTask(int _clientId, CTask* _task);

Не должно быть "повисших" клиентов, даже если клиентов больше, чем потоков.
Т.е. поток должен обрабатывать разных клиентов по-очереди, а не одного до завершения всех его задач.


Реализация(яндекс диск, архив С++ проекта на Visual Studio):

https://yadi.sk/d/PDLZZDPih3QT4

В архиве есть описание хода мыслей



Заранее спасибо за потраченное время.
Re[2]: Тестовое задание ...
От: Tesh США  
Дата: 12.06.15 21:31
Оценка:
Здравствуйте, AlexGin, Вы писали:

AG>Лично мне не понравилось, что члены классов никак не выделены:


Это не проблема, если вы код пишете не в блокноте.
Плюс, если начинаете путаться в переменных, то, видимо, метод уже слишком большой и его необходимо декомпозировать.
Re: Тестовое задание ...
От: antropolog  
Дата: 12.06.15 21:43
Оценка:
Здравствуйте, Selavi, Вы писали:

если вкратце — то сделано не то что требовалось. От слова совсем. Необходимо было реализовать класс CThreadPool с двумя методами по десять строк каждый, а вышел какой-то франкенштейн, непонятно что делающий. Ещё раз перечитайте задание, внимательно. И сделайте именно то, что требуется.
Re: Тестовое задание ...
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 12.06.15 22:43
Оценка:
Здравствуйте, Selavi, Вы писали:

S>...выполнено слабо

S>К сожалению, добиться обратной связи на тему "что именно слабо" не удалось.

S>Пожалуйста, покритикуйте реализацию.


1) слишком много классов, реализация размазана по трем классам, очень сложно читать
2) лишний поток для раскидывания задач, потоки — дорогое удовольствие.
3) отсутствует оповещение об окончании
4) из-за 3) неясно как будет освобождаться task
5) очередь стоило бы сделать lockfree
Re[3]: Тестовое задание ...
От: AlexGin Беларусь  
Дата: 12.06.15 23:43
Оценка:
Здравствуйте, Tesh, Вы писали:

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


AG>>Лично мне не понравилось, что члены классов никак не выделены:


T>Это не проблема, если вы код пишете не в блокноте.

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

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

Для production кодов, далеко не всегда имеет смысл декомпозировать метод. Это может привести к излишним сложностям.
В любом случае, метод может занимать более одного экрана.
Re: Тестовое задание ...
От: Selavi  
Дата: 13.06.15 06:04
Оценка:
Спасибо всем за ответы

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

Вот что меня несколько удивило:

antropolog писал:

A> если вкратце — то сделано не то что требовалось. От слова совсем. Необходимо было реализовать класс CThreadPool с двумя методами по десять строк каждый, а вышел какой-то франкенштейн, непонятно что делающий. Ещё раз перечитайте задание, внимательно. И сделайте именно то, что требуется.


Нигде в задании нет ни слова о 2х методах по десять строк) Но да бог с ним, я не об этом...

Честное слово, не представляю как можно реализовать ТЗ в 2х десятках строчек.
Для выполнения условия "Не должно быть "повисших" клиентов, даже если клиентов больше, чем потоков. Т.е. поток должен обрабатывать разных клиентов по-очереди, а не одного до завершения всех его задач" придется в любом случае вести список клиентов и на его основе вычислять следующее задание для свободного потока.

Или мсье может предложить алгоритм попроще?


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

A>1) слишком много классов, реализация размазана по трем классам, очень сложно читать

A>2) лишний поток для раскидывания задач, потоки — дорогое удовольствие.
A>3) отсутствует оповещение об окончании
A>4) из-за 3) неясно как будет освобождаться task
A>5) очередь стоило бы сделать lockfree

1) Сложно? Имо, наоборот, это как раз суть ООП — создавать абстракции и раскидывать их по классам. Если взять класс CThreadPoolX и просмотреть его методы, то становится однозначно понятно, что он делает. Я много раз видел, как люди запихивают все в 1 класс и это кошмар для понимания
2) Согласен, но без него будет подвисать пользователь пула потоков, что навряд ли правильно. Впрочем, тут вопрос производительности
3) Об окончании чего?
4) Не совсем понял о чем речь. Все task обернуты в shared_ptr и корректно освобождаются после удаления их из очередей. Есть, конечно, вариант, что они освободятся на стороне пользователя пула потоков, но знаете, это же все таки тестовое задание, а не реальная разработка.
5) Да, пожалуй, Вы правы.
Отредактировано 13.06.2015 8:32 DvaSL . Предыдущая версия . Еще …
Отредактировано 13.06.2015 8:31 DvaSL . Предыдущая версия .
Re[2]: Тестовое задание ...
От: antropolog  
Дата: 13.06.15 11:07
Оценка:
Здравствуйте, Selavi, Вы писали:

>Честное слово, не представляю как можно реализовать ТЗ в 2х десятках строчек.

поэтому ты и реализовал франкенштейна. А надо было всего-лишь реализовать простой паттерн — привязку продюсера к конкретному потоку. В данном случае это делается отдельной очередью на каждый поток, и помещением таски в очередь с индексом = taskID % threadNum
Re[2]: Тестовое задание ...
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 13.06.15 11:32
Оценка:
Здравствуйте, Selavi, Вы писали:

S>Или мсье может предложить алгоритм попроще?


На вскидку:

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

Диспетчерский метод (AddTask) по сути только разбрасывает маркеры клиентов по занятым или свободным потокам. Выборка заданий из очереди — ответственность WorkingThread. Будет небольшое "бутылочное горло" с совместным доступом к очереди заданий, но именно, что небольшое, вполне приемлемое для тестового задания.

Если будет желание, можно обыграть распределение заданий по разным очередям, lockfree, эвристики и т.п.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[3]: Тестовое задание ...
От: Selavi  
Дата: 13.06.15 12:08
Оценка:
Здравствуйте, antropolog, Вы писали:

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


>>Честное слово, не представляю как можно реализовать ТЗ в 2х десятках строчек.

A>поэтому ты и реализовал франкенштейна. А надо было всего-лишь реализовать простой паттерн — привязку продюсера к конкретному потоку. В данном случае это делается отдельной очередью на каждый поток, и помещением таски в очередь с индексом = taskID % threadNum

Сразу скажу, что отдельная очередь на каждый поток — самое плохое решение.
Почему?
Причин несколько:
1) Может возникнуть ситуация, когда у одного потока в очереди есть одно или больше заданий, хотя в это время же соседний поток свободен
2) Будут возникать ситуации параллельного выполнения заданий от 1го клиента. Это нарушение условия ТЗ и чтобы его исправить понадобится большем, чем 20 строк кода.
3) Чтобы реализовать равномерное распределение серверного времени по клиентам в Вашем варианте придется написать еще 20+ строчек кода

Вывод: Мсье торопится
Re[3]: Тестовое задание ...
От: Selavi  
Дата: 13.06.15 12:15
Оценка:
Здравствуйте, Геннадий Васильев, Вы писали:

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


S>>Или мсье может предложить алгоритм попроще?


ГВ>На вскидку:


ГВ>- Каждое задание снабжается маркером клиента;

ГВ>- Свободный поток получает маркер не обслуживаемого клиента и поочерёдно выбирает соответствующие задания из общей входной очереди;
ГВ>- Если свободного потока нет, добавляем маркер очередного клиента одному из занятых потоков (здесь можно вставить какую-нибудь несложную эвристику), после чего он начинает выбирать задания всех привязанных к нему клиентов.


Этот алгоритм не годится по причинам, описанным в моем посте выше

Основная проблема — мы не можем "на лету" вычислять следующее задание для потока, поскольку нам не известно время выполнения текущего задания. При таком способе мы придем к тому, что одни клиенты будут ждать свободный поток дольше других
Re[4]: Тестовое задание ...
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 13.06.15 12:31
Оценка:
Здравствуйте, Selavi, Вы писали:

S>Этот алгоритм не годится по причинам, описанным в моем посте выше


S>Основная проблема — мы не можем "на лету" вычислять следующее задание для потока, поскольку нам не известно время выполнения текущего задания.


А мы и не вычисляем его "на лету". Рабочий поток заканчивает выполнение очередного задания и переходит к поиску следующего.

S>При таком способе мы придем к тому, что одни клиенты будут ждать свободный поток дольше других


И в чём проблема? Это специфика ограничений задачи, не надо городить лишнего.

Допустим, у тебя пул на 3 потока, все они сейчас заняты клиентами с номерами, соответственно, 1..3. Появляется четвёртый. Куда его девать? Простейшее решение: подсунуть задания 4-го клиента первому же потоку, допустим, это поток номер 1. Тогда он станет выполнять задания в порядке: 1,4,1,4... Можно немного усложнить решение и отложить назначение 4-го клиента до момента, пока где-нибудь не завершится очередное задание — тогда поток, который первый начал поиск нового задания, заодно получит и нового клиента.

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

IRL можно накрутить диспетчеризацию в зависимости от загруженности потоков, статистики сложности заданий от клиентов и всё, что угодно.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[2]: Тестовое задание ...
От: Selavi  
Дата: 13.06.15 12:34
Оценка:
Здравствуйте, Геннадий Васильев, Вы писали:

ГВ>Вот это — очень и очень плохо:


ГВ>
TaskPtr *task = new TaskPtr(new CTaskDer(sec));


ГВ>Ещё немного по реализации.


ГВ>1) Лучше было бы сделать двухфазную инициализацию/разрушение: конструктор + init и shutdown + деструктор. Меня бы напрягло отсутствие такой "автоматической" реакции у интервьюируемого.


ГВ>2) Ключевое слово __interface? C++11? O'rly?


ГВ>3) Комментарии. Это вообще больной вопрос. Ребята, ну не надо издеваться над интервьюерами, а? Или пусть будет паритет: у одних код без комментариев, у других ответ без разъяснений.


ГВ>4) Тестовый пример лучше бы оформить в виде автоматизированных тестов, где, например, проверялась бы работа пула с заданиями нулевой длительности, с заданиями, выбрасывающими исключения и т.п. Диалоговый режим теста — это не тест, это demo.



1)
Да, я мог бы реализовать конструктор копирования в CTask и создавать копии заданий работы пула потоков (кстати, тоже можно придраться за лишние операции и расход памяти)
Да, я мог бы реализовать корректное хранение и удаление заданий вне пула потоков

Но есть же какое то разумное ограничение на время для тестового задания? Или мне надо было писать промышленную версию? Я просто не стал морочиться и кстати, написал это и в комменте к строчке и в описании к ТЗ. Почему то Вы на это не обратили внимания.

2) Что такого плохого в this->clientTaskFinisher = clientTaskFinisher?

3) Чем же лучше 2хфазная инициализацию/разрушение?

4) Вам не нравится использование интерфейсов в С++? Раздражает constructor injection? Что не так?

5) Да, с комментариями больной вопрос. Лично я придерживаюсь идеи дядюшки Боба о том, что комментарии засоряют код и нужно комментировать названиями методов и переменных. Считаю, что код в ТЗ вполне понятен и не требует лишней прозы.

6) Может и так, не буду спорить, хотя это снова мелочь.


Похоже на мелкие придирки, которые каждый может изобрести на свой вкус

Честно говоря, я ожидал, что будут замечания к алгоритму выборки клиентов, либо к реализации многопоточности в стиле "здесь можно было бы улучшить быстродействие"
А в итоге — все свелось к недовольству плохим стилем кода и отсутствием комментариев, хотя я написал аж целую сагу о том, как эта фигня работает.
Re[6]: Тестовое задание ...
От: antropolog  
Дата: 13.06.15 12:42
Оценка:
Здравствуйте, Selavi, Вы писали:

S>Опять какая то демагогия, которую я могу бы разбить в 2 счета, но, судя по всему, мсье — обычный хам, поэтому говорить с ним не хочется.


с воинствующим невежеством общаться вообще нет резона. ариведерчи.
Re[4]: Тестовое задание ...
От: Selavi  
Дата: 13.06.15 13:41
Оценка:
Да, так понятней)

Спасибо за Ваше время!
Re[2]: Тестовое задание ...
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 13.06.15 15:23
Оценка:
Здравствуйте, Selavi, Вы писали:


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


A>>1) слишком много классов, реализация размазана по трем классам, очень сложно читать

A>>2) лишний поток для раскидывания задач, потоки — дорогое удовольствие.
A>>3) отсутствует оповещение об окончании
A>>4) из-за 3) неясно как будет освобождаться task
A>>5) очередь стоило бы сделать lockfree

S>1) Сложно? Имо, наоборот, это как раз суть ООП — создавать абстракции и раскидывать их по классам. Если взять класс CThreadPoolX и просмотреть его методы, то становится однозначно понятно, что он делает. Я много раз видел, как люди запихивают все в 1 класс и это кошмар для понимания

Конечно сложно. Из-трех классов у тебя примерно в 3 раза больше кода, чем стоило бы написать. Никакое ооп не является оправданием. Избыток кода сильно затрудняет понимание.

S>2) Согласен, но без него будет подвисать пользователь пула потоков, что навряд ли правильно. Впрочем, тут вопрос производительности

Ничего подвисать не будет, если правильно писать.

S>3) Об окончании чего?

Выполнении задачи конечно.

S>4) Не совсем понял о чем речь. Все task обернуты в shared_ptr и корректно освобождаются после удаления их из очередей. Есть, конечно, вариант, что они освободятся на стороне пользователя пула потоков, но знаете, это же все таки тестовое задание, а не реальная разработка.

Ты получаешь указатель, и не сообщаешь вызываемому коду когда задача завершена. Внимание вопрос: когда освобождать CTask?

Я бы на твоем месте нагуглил хороший пример, а потом бы допилил алгоритм распределения задач.
Re[4]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 13.06.15 18:33
Оценка:
Здравствуйте, Геннадий Васильев, Вы писали:

S>>3) Чем же лучше 2хфазная инициализацию/разрушение?

ГВ>Проще контролировать вылетающие исключения.

close/clear/finish — возможно (хотя подобное достигается через optional или default constructor + move assignment), но двухфазная инициализация точно не нужна.
Re[3]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 13.06.15 20:00
Оценка:
Здравствуйте, Selavi, Вы писали:

EP>>Тут никакой *_ptr вообще не нужен. Так обычно пишут Java/C# программисты не знающие идиом C++.

EP>>Советую начать с чтения книжек Страуструпа.
S>И сколько же мне нужно прочитать книжек Страуструпа, чтобы найти идиому С++, которая объяснит почему тут не нужен умный указатель?

Достаточно одной TC++PL

S>Наверное потому, что память все равно очистится при выходе из программы?


Нет. Потому что вот это
std::unique_ptr<CThreadPoolX> pool (new CThreadPoolX(5));
заменяется на:
CThreadPoolX pool(5);

На C++ в большинстве случаев не нужны никакие владеющие указатели (ни умные, ни тем более простые).

S>Почему нельзя было просто это сказать, а не делать высокомерную отсылку к Страуструпу?


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

S>Полезность такого поста нулевая.


Я дал совет который считаю что даст максимальный полезный эффект. Конечно твоё право им не воспользоваться.
Re[4]: Тестовое задание ...
От: Selavi  
Дата: 13.06.15 20:07
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

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


EP>>>Тут никакой *_ptr вообще не нужен. Так обычно пишут Java/C# программисты не знающие идиом C++.

EP>>>Советую начать с чтения книжек Страуструпа.
S>>И сколько же мне нужно прочитать книжек Страуструпа, чтобы найти идиому С++, которая объяснит почему тут не нужен умный указатель?

EP>Достаточно одной TC++PL


S>>Наверное потому, что память все равно очистится при выходе из программы?


EP>Нет. Потому что вот это

EP>
EP>std::unique_ptr<CThreadPoolX> pool (new CThreadPoolX(5));
EP>
заменяется на:

EP>
EP>CThreadPoolX pool(5);
EP>

EP>На C++ в большинстве случаев не нужны никакие владеющие указатели (ни умные, ни тем более простые).

S>>Почему нельзя было просто это сказать, а не делать высокомерную отсылку к Страуструпу?


EP>Потому что помимо этого базового пробела наверняка есть множество других. Пытаться фиксить их один за одним на форуме — малопродуктивно.


S>>Полезность такого поста нулевая.


EP>Я дал совет который считаю что даст максимальный полезный эффект. Конечно твоё право им не воспользоваться.


Ясно теперь)
Спасибо!
Re[2]: Тестовое задание ...
От: Олег К.  
Дата: 14.06.15 07:58
Оценка:
С>Итого — лучше иметь возможность беседовать с интервьювером напрямую и постараться вести разговор самому, то есть — задавать тему. Любой человек может задать кучу вопросов, на которую не ответит любой собеседник. Постарайтесь донести до интервьювера мысль, что спрашивать надо о том, что человек знает, а не пытаться намеренно убедиться в его невежесте.

До таких вряд ли что дойдет.
Re[2]: Тестовое задание ...
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 14.06.15 08:58
Оценка:
Здравствуйте, Слава, Вы писали:

С>К сожалению, в современном С++ я ничего не понимаю. Но, судя по комментариям, вы в задании не выполнили некие ритуалы, которые должны выполняться, но о которых не пишут в книгах, считая оные ритуалы самоочевидными. Также, в каждой конторе эти ритуалы слегка отличаются. То есть, от вас хотят видеть то же, что они хотели видеть в своих проектах. Это не означает, что в проектах у них это самое имеется, там может быть (и скорее всего есть) ужасная кодовая лапша. Однако же, со своей кодовой кодовой базой они сделать ничего не могут, зато могут требовать от соискателя соответствия неким идеалам, описанным только у них в голове.


С>Итого — лучше иметь возможность беседовать с интервьювером напрямую и постараться вести разговор самому, то есть — задавать тему. Любой человек может задать кучу вопросов, на которую не ответит любой собеседник. Постарайтесь донести до интервьювера мысль, что спрашивать надо о том, что человек знает, а не пытаться намеренно убедиться в его невежесте.


Странная логика. И наниматель, и кандидат ищут друг в друге нечто более или менее определённое. Почему при этом именно работодатель должен старательно обходить сложные для кандидата моменты — сие неведомо. Простите, а кандидат тоже должен бояться спросить у работодателя, например, соблюдает ли тот КЗоТ? А то, вдруг не соблюдает и страшно оскорбится таким вопросом.

И потом, то, что в этом топике сказали топикстартеру, это не какие-то специфические "ритуалы", доступные только посвящённым, а в основном банальные, уже давно устоявшиеся правила хорошего тона с небольшой поправкой на ситуацию собеседования. Естественно, все понимают, что в production-коде может царить хаос и кабакЪ, но это же не означает, что на собеседовании нужно заниматься тем же самым.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[3]: Тестовое задание ...
От: Слава  
Дата: 14.06.15 13:56
Оценка:
Здравствуйте, Геннадий Васильев, Вы писали:

ГВ>Странная логика. И наниматель, и кандидат ищут друг в друге нечто более или менее определённое. Почему при этом именно работодатель должен старательно обходить сложные для кандидата моменты — сие неведомо. Простите, а кандидат тоже должен бояться спросить у работодателя, например, соблюдает ли тот КЗоТ? А то, вдруг не соблюдает и страшно оскорбится таким вопросом.


Я не знаю, давно ли вы меняли работу и приходили на новое место "с улицы", "по объявлению". Очень много есть гнилых контор, где люди хотят непонятно чего. Иногда, своего клона. Иногда, они вообще отсекают всех кандидатов, потому что им не нужны лишние люди, а причина отказа всегда найдется (см. анекдот: экзамен по истории — перечислите поименно всех погибших на великой отечественной)

Дано — работа, для ее выполнения требуются навыки н1..н10. У собеседующего есть навыки н6..н15, при этом от соискателя требуются по работе н1..н5 и возможно — в будущем, н16-н17, про это будущее интервьюер знает, но не помнит. По какому-то стечению обстоятельств он начинает опрашивать соискателя по тем знаниям, которые он почему-то считает более важными и это могут быть н9..н15, хотя для работы требуются именно н1..н10. Соискатель, если он займет пассивную позицию отвечающего на вопросы, может вообще не рассказать, что он знает н1..н4, н16..н17, его опросят по другой области и посчитают негодным.

Как пример — приходит человек CRUD лепить, а его зачем-то спрашивают про map-reduce, и просят выдумать алгоритм сортировки, а затем оценить его сложность, при том, что интервьюер сам занимался этим только в универе, если вообще занимался, а то ведь он может просто считать это важным, потому что Кнут! Дейкстра! Линус! SICP! Computer Science!, но применимо оно в большинстве проектов, как феррари в деревне — полюбовлся, и пошел дальше на тракторе работать. Задают мутные вопросы "что такое REST" (на это хорошим контрвопросом был бы "что такое говно" из сказок Дмитрия Гайдука), потому что интервьюера беспокоит, что их нынешний REST не канониченЪ, и он хочет найти себе единомышленника (даже если найдет, приведения к КанонуЪ все равно не состоится, потому что жизнь борьба). Еще хороший вопрос, на который невозможно ответить целиком и верно — "расскажите про протокол HTTP" (вы его диаграмму видели? а наизусть?).

Мое сообщение больше касалось именно личной беседы, а не тестового задания. В случае беседы зачастую бывает так, что от соискателя требуется быстрое решение и прямо сейчас, и верное. Но — этого ведь просто не бывает! Срочность в IT бывает только у админов (бэкап восстановить), а срочно чего-то изобрести — простите, тут не кино про хакеров, которые за 5 минут сервера ломают. Таких скорых задач нужно избегать, а если не получится, то откладывать их до того момента, когда уже сложится благоприятное впечатление. Именно поэтому, тестовое задание — хорошая мысль, если на его выполнение отводится достаточное время и возможность делать его дома в IDE, а не в офисе на бумажке. И только после беседы, а не до нее, потому что описываемые мной "местные ритуалы" проще выяснить на месте, чем потом доставать интервьюера письмами.

ГВ>И потом, то, что в этом топике сказали топикстартеру, это не какие-то специфические "ритуалы", доступные только посвящённым, а в основном банальные, уже давно устоявшиеся правила хорошего тона с небольшой поправкой на ситуацию собеседования. Естественно, все понимают, что в production-коде может царить хаос и кабакЪ, но это же не означает, что на собеседовании нужно заниматься тем же самым.


Очень многие программисты не в состоянии чего-то кому-то объяснить. С человеком поговорить, человеческим языком. Если у человека есть знания, но нет стиля, то почему бы его стилю попросту не научить? Нет, мы будем искать идеал полгода и возмущаться, что нет людей, кругом одни идиоты, мысли читать не умеют.
Re[5]: Тестовое задание ...
От: Олег К.  
Дата: 14.06.15 22:36
Оценка:
ГВ>Что, для того, чтобы буквально выполнить задание и скопировать прототип функции нужно развозить, извини меня, сопли на километр? Специально планировать стратегию собеседования? Копаться в тоннах мануалов? Ещё что-то?

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

Ну и задание само так себе. Ну и сам факт что они дают задание говорит, что чуваки просто не умеют поговорить нормально на интервью.
Re[5]: Тестовое задание ...
От: Олег К.  
Дата: 14.06.15 22:42
Оценка:
S>>Кстати, неприятно, что в этой конторе сидят жлобы, которые даже не удосужились ответить. Подумаешь, кто-то там потратил N часов на ТЗ. Не понравилось, значит игнорим. Рашка, блин.

ГВ>Согласен, если уж ты сам спрашивал о причинах непрохождения теста, могли бы и объяснить, хотя бы кратко. Высокомерно молчать — просто невежливо и уважения не добавляет.


Более того, давать само задание невежливо. Обе стороны, имхо, должны тратить одинаковое количество времени на интервью.

ТС, тебе просто попались очередные чудаки с самомнением. В следущий раз не делай просто задания и все. Ну и озвучь тут название конторы.
Re[5]: Тестовое задание ...
От: Олег К.  
Дата: 14.06.15 22:48
Оценка:
S>>Работая над этим ТЗ я потратил большую часть времени потратил на обдумывание алгоритма, который удовлетворяет требованиям задачи так, как я их вижу.
G>Чуть со стула не упал когда прочитал. Какая разница как ты видишь требования. Человек который не может выяснить\понять что нужно заказчику и тратит большую часть времени на "обдумывание алгоритма" вообще не должен работать программистом.

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

S>>Кстати, неприятно, что в этой конторе сидят жлобы, которые даже не удосужились ответить. Подумаешь, кто-то там потратил N часов на ТЗ. Не понравилось, значит игнорим. Рашка, блин.

G>Ага, рашка во всем виновата и жлобы. А ты вообще на форум то зачем написал? Пожаловаться? Тогда зачем пример кода показывал? Жаловаться проще когда фактов нет.

Ну он в принципе прав (если опустить слово "Рашка"). Такого безосновательного самомнения как у российских программистов я ни у кого из других национальностей не видел.
Re[5]: Тестовое задание ...
От: Selavi  
Дата: 14.06.15 23:22
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Чуть со стула не упал когда прочитал. Какая разница как ты видишь требования. Человек который не может выяснить\понять что нужно заказчику и тратит большую часть времени на "обдумывание алгоритма" вообще не должен работать программистом.

G><skipped>

Да ну, опять демагогия какая то.

Сама постановка задачи предполагает множество трактовок. Не вижу смысла в трате времени для уточнения условий Тестового Задания, равно как и в переборе всех возможных вариантов, поэтому выбираю вариант, который мне нравится больше и реализую его. В данном случае — вариант с наиболее "долгождушим" клиентом.

И...вы вообще не обдумываете алгоритмы? Просто лепите первое что придет в голову? Честно, не понял замечания.


G>А на мой взгляд ты вообще не решил исходную задачу. Там требовалось сделать вполне определенный API и оптимальную загрузку ресурсов. А у тебя лишние потоки и API не соответствует заданию. Ну и кому нужен твой алгоритм в итоге? Кроме того ты не уточнил задачу, возможно там требовался thread affinity для каждого клиента.


Что для вас "оптимальная загрузка ресурсов"? Для меня в данном ТЗ — минимизация времени ожидания клиента. И поверьте, что если это не так, то я без проблем реализую другой вариант.

G>Действительно всем плевать на алгоритм и на ход размышлений.

G>На что не плевать:
G>1) Понимание решаемой задачи
G>2) Точное выполнение задачи

Вам наверное задачи ставят боги)
А вот мне на текущем месте работы задачи ставят люди и эти нередко ошибаются или сами не знают чего хотят, поэтому я зачастую сам предлагаю вносить изменения в ТЗ. И не поверите — соглашаются и даже довольны.

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

Ну да. Хотел и сделал. И да, не оценили потому что такие как вы тут сидят не для того, чтобы оценивать, а для того, чтобы тешить свое эго. В этом топике 40+ ответов и из них только 3 действительно несут полезную нагрузку.

G>Ага, рашка во всем виновата и жлобы. А ты вообще на форум то зачем написал? Пожаловаться? Тогда зачем пример кода показывал? Жаловаться проще когда фактов нет.

эммм...даже не знаю что на это ответить. Вы читали 1й пост?
Re[6]: Тестовое задание ...
От: Selavi  
Дата: 14.06.15 23:29
Оценка:
Здравствуйте, Олег К., Вы писали:

S>>>Кстати, неприятно, что в этой конторе сидят жлобы, которые даже не удосужились ответить. Подумаешь, кто-то там потратил N часов на ТЗ. Не понравилось, значит игнорим. Рашка, блин.


ГВ>>Согласен, если уж ты сам спрашивал о причинах непрохождения теста, могли бы и объяснить, хотя бы кратко. Высокомерно молчать — просто невежливо и уважения не добавляет.


ОК>Более того, давать само задание невежливо. Обе стороны, имхо, должны тратить одинаковое количество времени на интервью.


ОК>ТС, тебе просто попались очередные чудаки с самомнением. В следущий раз не делай просто задания и все. Ну и озвучь тут название конторы.


Да я тоже так думаю)
Задания обычно не делаю, но тут было свободное время.

Контора — Panzar
Re[6]: Тестовое задание ...
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 15.06.15 00:03
Оценка:
Здравствуйте, Олег К., Вы писали:

ОК>Ну он в принципе прав (если опустить слово "Рашка"). Такого безосновательного самомнения как у российских программистов я ни у кого из других национальностей не видел.


Открою тайну — те кто проводят собеседования и те кто ходят по собеседованиям — одни и те же люди, просто в разные моменты времени. По этому жаловаться на других программистов, тут же проявляя высокомерие — глупо как минимум.
Re[7]: Тестовое задание ...
От: Олег К.  
Дата: 15.06.15 02:19
Оценка:
G>Открою тайну — те кто проводят собеседования и те кто ходят по собеседованиям — одни и те же люди, просто в разные моменты времени. По этому жаловаться на других программистов, тут же проявляя высокомерие — глупо как минимум.

Открою тайну — не все программисты устраивают экзамен вместо интервью. Соответственно, те кто начинают задавать подобные задания или вопросы в духе "а что такое интерфейс?", "а как работает сборщик мусора?", "а какая сложность поиска в дереве?" и прочая хрень — больше ничего собственно и не могут. Высший пилотаж это уметь поговорить с кандидатом на равных, но таких людей уничтожающе мало в последнее время. Ну и не считать себя самым умным.
Re[7]: Тестовое задание ...
От: Олег К.  
Дата: 15.06.15 02:26
Оценка:
S>>Сама постановка задачи предполагает множество трактовок. Не вижу смысла в трате времени для уточнения условий Тестового Задания, равно как и в переборе всех возможных вариантов, поэтому выбираю вариант, который мне нравится больше и реализую его. В данном случае — вариант с наиболее "долгождушим" клиентом.
G>И это no hire. Неважно что нравится тебе, важно что понравится заказчику. То что ты сделал — не понравилось. Тебе кучу причин уже привели почему оно могло не понравится.

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

S>>И поверьте, что если это не так, то я без проблем реализую другой вариант.

G>Такая фраза не делает тебя специалистом. Кто мешал сразу уточнить детали и хотя бы API сделать ровно как написано?

Кто мешал нормально поставить задачу?

G>>>Ага, рашка во всем виновата и жлобы. А ты вообще на форум то зачем написал? Пожаловаться? Тогда зачем пример кода показывал? Жаловаться проще когда фактов нет.

S>>эммм...даже не знаю что на это ответить. Вы читали 1й пост?
G>Читал, поэтому и спрашиваю. Ты хотел показать себя, а не получить работу. Показал, но работодателю оказалось все равно. Зачем жаловаться?

Имеет полное право. Он потратил на них свое время. Соответсвенно пусть и они потратят на него свое время. Иначе это игра в одни ворота. Ну и пусть сами решат свое же задание а мы тут покритикуем.
Re[7]: Тестовое задание ...
От: Олег К.  
Дата: 15.06.15 04:11
Оценка:
ОК>>Такого безосновательного самомнения как у российских программистов я ни у кого из других национальностей не видел.

ГВ>Точно-точно, я даже пару примеров могу привести:


ГВ>Вот это первый
Автор: Олег К.
Дата: 15.06.15
:


ГВ>

Я его код не смотрел, но то что ты говоришь — это мелочи все.


И что тебе не понравилось в этом комментарии?

ГВ>А вот второй
Автор: Слава
Дата: 13.06.15
:


ГВ>

К сожалению, в современном С++ я ничего не понимаю. Но, судя по комментариям ...


А это уже не мой комментарий.

ГВ>Оснований, как ты понимаешь, у обоих высказавшихся недостаточно, но они этого нисколько не стесняются. Даже прямо говорят о том, что рассуждать будут безосновательно. Ну что ж... Честность — лучшая политика!


Ты о чем вообще?
Re[7]: Тестовое задание ...
От: Олег К.  
Дата: 15.06.15 05:09
Оценка:
ОК>>Ну и задание само так себе.

ГВ>Задание из разряда простейших и поставлено вполне внятно. Не нравится — не делай. На мой взгляд, в нём ничего нет такого, что вызывало бы острое неприятие.


Ты не догоняешь ведь. Чуваки не умеют поговорить, поэтому и дают дурацкое задание. Не имеет значение на день оно или на неделю. ТС потратил на них время. Вполне справедливо с его стороны ожидать хотя бы минимальный фидбэк от них.

ОК>>Ну и сам факт что они дают задание говорит, что чуваки просто не умеют поговорить нормально на интервью.


ГВ>Топикстартер поставил конкретный вопрос и получил на него вполне (как мне кажется) содержательные ответы. Если тебе хочется порассуждать об отвлечённом — никто не вправе тебе помешать.


Получил. Я в свою очередь добавил, что некоторые из вещей — мелочи на который нормальный инженер не будет обращать внимания. Ну и спасибо за разрешение.
Re[8]: Тестовое задание ...
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 15.06.15 05:15
Оценка:
Здравствуйте, Олег К., Вы писали:

ГВ>>Вот это первый
Автор: Олег К.
Дата: 15.06.15
:

ГВ>>

Я его код не смотрел, но то что ты говоришь — это мелочи все.

ОК>И что тебе не понравилось в этом комментарии?

То, что ты не потрудился изучить первоисточник, но уже предъявил своё мнение об одной из сторон.

ГВ>>А вот второй
Автор: Слава
Дата: 13.06.15
:

ГВ>>

К сожалению, в современном С++ я ничего не понимаю. Но, судя по комментариям ...

ОК>А это уже не мой комментарий.

Естественно, не твой. Я привёл это высказывание как пример аналогичного подхода.

ГВ>>Оснований, как ты понимаешь, у обоих высказавшихся недостаточно, но они этого нисколько не стесняются. Даже прямо говорят о том, что рассуждать будут безосновательно. Ну что ж... Честность — лучшая политика!


ОК>Ты о чем вообще?


Пытаюсь показать тебе, что ты сам и демонстрируешь поведение, за которое упрекаешь других.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[8]: Тестовое задание ...
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 15.06.15 05:23
Оценка:
Здравствуйте, Олег К., Вы писали:

ОК>ТС потратил на них время. Вполне справедливо с его стороны ожидать хотя бы минимальный фидбэк от них.


Да, тут я с тобой согласен.

ОК>Я в свою очередь добавил, что некоторые из вещей — мелочи на который нормальный инженер не будет обращать внимания.


Это уже просто праздник какой-то! То есть "нормальный инженер" не обращает внимание на отклонения реализации от письменного задания?
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[9]: Тестовое задание ...
От: Олег К.  
Дата: 15.06.15 05:32
Оценка:
ГВ>>>Вот это первый
Автор: Олег К.
Дата: 15.06.15
:

ГВ>>>

Я его код не смотрел, но то что ты говоришь — это мелочи все.

ОК>>И что тебе не понравилось в этом комментарии?

ГВ>То, что ты не потрудился изучить первоисточник, но уже предъявил своё мнение об одной из сторон.


А какое мнение я предъявил? Мне кажется ты все еще не догоняешь о чем я.

ГВ>>>А вот второй
Автор: Слава
Дата: 13.06.15
:

ГВ>>>

К сожалению, в современном С++ я ничего не понимаю. Но, судя по комментариям ...

ОК>>А это уже не мой комментарий.

ГВ>Естественно, не твой. Я привёл это высказывание как пример аналогичного подхода.


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

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

ГВ>>>Оснований, как ты понимаешь, у обоих высказавшихся недостаточно, но они этого нисколько не стесняются. Даже прямо говорят о том, что рассуждать будут безосновательно. Ну что ж... Честность — лучшая политика!


ОК>>Ты о чем вообще?


ГВ>Пытаюсь показать тебе, что ты сам и демонстрируешь поведение, за которое упрекаешь других.


Это что же я демонстрирую? Наоборот, у меня нет эго и я могу поговорить с человеком без всяких заданий. А вот задающие такие задания (вообще любые а не конкретно это) считают себя более умными хотя более ни на что и не способны.
Re[10]: Тестовое задание ...
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 15.06.15 05:46
Оценка:
Здравствуйте, Олег К., Вы писали:

ГВ>>То, что ты не потрудился изучить первоисточник, но уже предъявил своё мнение об одной из сторон.

ОК>А какое мнение я предъявил? Мне кажется ты все еще не догоняешь о чем я.

Гы:

Ну и сам факт что они дают задание говорит, что чуваки просто не умеют поговорить нормально на интервью.


Я бы всё же, посоветовал тебе самостоятельно сравнить задачу и реализацию. Глядишь, мнение бы и поменялось.

ГВ>>Естественно, не твой. Я привёл это высказывание как пример аналогичного подхода.

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

Да, это отличное новое знание. Только какое отношение оно имеет к вопросу ТСа?

ГВ>>Пытаюсь показать тебе, что ты сам и демонстрируешь поведение, за которое упрекаешь других.

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

Прикольно. То есть попытка сократить время на оценку кандидата путём выдачи короткого задания — это признак гипертрофированного эго?

ОК>А вот задающие такие задания (вообще любые а не конкретно это) считают себя более умными хотя более ни на что и не способны.


Ты всё ещё утверждаешь, что у тебя "нет эго"? Продолжай в том же духе, мне нравится твой стиль.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[10]: Тестовое задание ...
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 15.06.15 05:55
Оценка:
Здравствуйте, Олег К., Вы писали:

ОК>>>Я в свою очередь добавил, что некоторые из вещей — мелочи на который нормальный инженер не будет обращать внимания.

ГВ>>Это уже просто праздник какой-то! То есть "нормальный инженер" не обращает внимание на отклонения реализации от письменного задания?
ОК>Если задание может быть непонято, то оно будет непонято! Поэтому во многих случаях явно обращают внимание читающего. Где в задании было написано, что отклоняться от интерфейса нельзя?

Праздник плавно перерастает в феерию!

ОК>Где другие детали описывающие какой должна быть реализация? Я привел выше несколько вопросов. А то задача сформулирована всего в пару строчек, так тут еще и юнит тесты от ТС желают!


А зачем тебе требования к деталям реализации? Ты же всё равно потом скажешь, что, мол, не написано, что эти требования надо обязательно выполнять.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[11]: Тестовое задание ...
От: Олег К.  
Дата: 15.06.15 06:09
Оценка:
ГВ>Гы:

ГВ>

Ну и сам факт что они дают задание говорит, что чуваки просто не умеют поговорить нормально на интервью.


ГВ>Я бы всё же, посоветовал тебе самостоятельно сравнить задачу и реализацию. Глядишь, мнение бы и поменялось.


Я верю вашим комментариям, но... Обрати внимание на "но." Мой посыл был в том, что интервьюеры могли бы установить на интервью подходит им кандидат или нет не напрягая кандидата своим заданием. Далее, он сделал задание, и пусть там будет самый худший код, но они даже не удосужились ответить ему. Что о них еще можно сказать?

ГВ>>>Естественно, не твой. Я привёл это высказывание как пример аналогичного подхода.

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

ГВ>Да, это отличное новое знание. Только какое отношение оно имеет к вопросу ТСа?


Да самое прямое. В отношении него была совершена несправедливость. Я объяснил причину этой несправедливости.

ГВ>>>Пытаюсь показать тебе, что ты сам и демонстрируешь поведение, за которое упрекаешь других.

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

ГВ>Прикольно. То есть попытка сократить время на оценку кандидата путём выдачи короткого задания — это признак гипертрофированного эго?


Э-э-э... Ты подумать над сказанным не хочешь? Кандидату приходится тратить свое время только потому что они не могут провести интервью! Им там (и тебе тоже) нужно пять минут на просмотр кода, но ведь кандидат-то больше времени потратил на его написание? Так что это получается игра в одни ворота. Типа "умные" чуваки будут решать подходит им кандидат или нет, но однако они лишили кандидата возможности увидеть их код.

ОК>>А вот задающие такие задания (вообще любые а не конкретно это) считают себя более умными хотя более ни на что и не способны.


ГВ>Ты всё ещё утверждаешь, что у тебя "нет эго"? Продолжай в том же духе, мне нравится твой стиль.


Ты заметь, в данной ветке только два человека сказали, что такой экзамен — ненормально. Остальные набросились на ТС, пусть он тысячу раз неправ.

Для этого, к сведению, и нужно интервью, чтобы обоим сторонам понять насколько они подходят друг другу. А тут кандидат пахал а те чуваки решили за пять минут (тебе ведь пять минут понадобилось, чтобы взглянуть на его код?) чтобы отсеять его.
Re[11]: Тестовое задание ...
От: Олег К.  
Дата: 15.06.15 06:15
Оценка:
ОК>>>>Я в свою очередь добавил, что некоторые из вещей — мелочи на который нормальный инженер не будет обращать внимания.
ГВ>>>Это уже просто праздник какой-то! То есть "нормальный инженер" не обращает внимание на отклонения реализации от письменного задания?
ОК>>Если задание может быть непонято, то оно будет непонято! Поэтому во многих случаях явно обращают внимание читающего. Где в задании было написано, что отклоняться от интерфейса нельзя?

ГВ>Праздник плавно перерастает в феерию!


Чувак, ты бы не позорился, что ли? Я видел нормально изложенные задания и видел так, чтобы отвязаться. А есть вообще устные. Это из второй оперы.

На этом форуме мне понравилось как х64 описывал свои пожелания.

ОК>>Где другие детали описывающие какой должна быть реализация? Я привел выше несколько вопросов. А то задача сформулирована всего в пару строчек, так тут еще и юнит тесты от ТС желают!


ГВ>А зачем тебе требования к деталям реализации? Ты же всё равно потом скажешь, что, мол, не написано, что эти требования надо обязательно выполнять.


Не напишу. Это я тебе говорю к тому, что от кандидата хотят многого, а сами четко составить задание не в состоянии. Но до тебя ведь это не доходит?
Re[8]: Тестовое задание ...
От: Олег К.  
Дата: 15.06.15 06:25
Оценка:
ОК>>Пулу при инициализации задается число рабочих потоков:
ОК>>bool CThreadPool::Init(int _numThreads);

ОК>>Почему количество потоков не задается в конструкторе?


ГВ>Стереотипичный ответ: потому что предполагается двухфазная инициализация.


Ну и нафига? Уж лучше было бы эту функцию назвать что-нибудь вроде CThreadPool::StartThreads() чтобы изменить семантику. Потоки ведь нао будет еще как-то останавливать, уничтожать, ждать их завершения и т.д.

ОК>>Что должна вернуть функция?


ГВ>Внезапно: true, если все в порядке и false, если не удалось создать все запланированные потоки.


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

ОК>>Что делать если нельзя создать нужное количество потоков? Убивать уже созданные или оставить их так как есть? Где описание всего этого?


ГВ>Ответы простейшие: а) вернуть false, б) убрать созданные.


Смотри выше. Ну и где описание оного?

ОК>>Почему от ТС требуются юнит-тесты а составители не желают четко описать чего хотят?


ГВ>Потому что не нужно быть семи пядей во лбу, чтобы ответить на поставленные вопросы.


То есть получается, что кто-то желает многого но сам хочет сделать малого (составить нормальное задание)?

ОК>>В каком состоянии должны быть потоки? Running или suspended? Где описание?


ГВ>Это можно спросить у тех, кто дал задание. Но подразумевается, что кандидат сам выберет то, что ему по вкусу.


Почему эта ситуация не была описана в задании? Далее, почему тут можно кандидату выбирать по вкусу а изменить интерфейс как ему нравится нельзя?

ОК>>Что делает подчеркивание перед параметром? Обычно такая хрень — спереди или сзади — вводится для членов. Какая у них венгерская нотация для самих локальных переменных и членов?


ГВ>Стиль внешнего оформления задан, внутри пиши как хочешь или уточни у тех, кто дал задание.


Какой-то хреновый стиль но пусть будет по-твоему. Почему не описан внутренний стиль?

ОК>>Там еще много чего можно сказать о самом задании.


ГВ>Угу, заметно.


Как там в КУ было? "Поставь минус и улыбнись."
Re[7]: Тестовое задание ...
От: Selavi  
Дата: 15.06.15 06:32
Оценка:
S>>Здравствуйте, gandjustas, Вы писали:

G>И это no hire. Неважно что нравится тебе, важно что понравится заказчику. То что ты сделал — не понравилось. Тебе кучу причин уже привели почему оно могло не понравится.

т.е. это no hire не потому, что я плохо пишу код, а потому что я не уточнил у заказчика детали?) Что ж, у меня еще есть надежда)

G>Нет, я тупо копирую готовые рабочие решения, а потом допиливаю детали.

А если готового решения нет?

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

В моей реализации потоки не простаивают и выполняют задания клиентов. Плюс минимизируется время ожидания. Что в этом плохого?

G>Такая фраза не делает тебя специалистом. Кто мешал сразу уточнить детали и хотя бы API сделать ровно как написано?

Я уже писал об этом. ТЗ было получено через КА и мне совершенно не хотелось играть в испорченный телефон, поэтому я сам придумал вариант. В реальной обстановке, конечно же, я бы долбил заказчика до тех пор, пока ТЗ(техническое задание, не тестовое) не стало бы однозначным, что, собственно, я и делаю постоянно на текущей работе. Мне не понятно почему из этого момента раздувается такая буча.

G>И что? То что ты тут пишешь — ничего не решает. Ты же не предложил внести изменения, ты просто сделал как тебе захотелось.

см выше

G>Собеседование не для того, чтобы оценивать твое творчество. Зачем жаловаться?

Собеседование нужно для оценки кандидата работодателем и работодателя кандидатом. В то числе через их творчество.

G>Читал, поэтому и спрашиваю. Ты хотел показать себя, а не получить работу. Показал, но работодателю оказалось все равно. Зачем жаловаться?

Опять эта ерунда про жалобу. Я сделал ТЗ и не получил фидбека. Это некрасиво со стороны работодателя и я написал об этом, чтобы предостеречь других.
Re[9]: Тестовое задание ...
От: Selavi  
Дата: 15.06.15 06:38
Оценка:
ОК>Почему эта ситуация не была описана в задании? Далее, почему тут можно кандидату выбирать по вкусу а изменить интерфейс как ему нравится нельзя?

Собственно, прямо в точку. В соседней ветке я говорил кому то, что ТЗ было получено через кадровое и мне не хотелось устраивать испорченный телефон по уточнению задания.
Кстати, если бы ТЗ мне дали бы на собеседовании, то я бы, однозначно, задал уточняющие вопросы по нему и скорее всего этого топика бы не было.
Re[9]: Тестовое задание ...
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 15.06.15 07:10
Оценка:
Здравствуйте, Олег К., Вы писали:

ОК>>>Пулу при инициализации задается число рабочих потоков:

ОК>>>bool CThreadPool::Init(int _numThreads);
ОК>>>Почему количество потоков не задается в конструкторе?
ГВ>>Стереотипичный ответ: потому что предполагается двухфазная инициализация.
ОК>Ну и нафига? Уж лучше было бы эту функцию назвать что-нибудь вроде CThreadPool::StartThreads() чтобы изменить семантику. Потоки ведь нао будет еще как-то останавливать, уничтожать, ждать их завершения и т.д.

А какая тебе разница? Задание поставлено именно так, и никак иначе. Естественным следствием из наличия Init будет появление и Shutdown.

ОК>>>Что должна вернуть функция?

ГВ>>Внезапно: true, если все в порядке и false, если не удалось создать все запланированные потоки.
ОК>Это твои домыслы (с коими я сам также согласен), но дай я поутрирую чуточку. Это ж сколько тебе нужно создать потоков, чтобы функция вернула ложь?

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

ОК>>>Что делать если нельзя создать нужное количество потоков? Убивать уже созданные или оставить их так как есть? Где описание всего этого?

ГВ>>Ответы простейшие: а) вернуть false, б) убрать созданные.
ОК>Смотри выше. Ну и где описание оного?

Это стереотип, которого, ИМХО, вполне достаточно для тестового задания.

ОК>>>Почему от ТС требуются юнит-тесты а составители не желают четко описать чего хотят?

ГВ>>Потому что не нужно быть семи пядей во лбу, чтобы ответить на поставленные вопросы.
ОК>То есть получается, что кто-то желает многого но сам хочет сделать малого (составить нормальное задание)?

Да нормальное оно вполне.

ОК>>>В каком состоянии должны быть потоки? Running или suspended? Где описание?

ГВ>>Это можно спросить у тех, кто дал задание. Но подразумевается, что кандидат сам выберет то, что ему по вкусу.
ОК>Почему эта ситуация не была описана в задании? Далее, почему тут можно кандидату выбирать по вкусу а изменить интерфейс как ему нравится нельзя?

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

ОК>>>Что делает подчеркивание перед параметром? Обычно такая хрень — спереди или сзади — вводится для членов. Какая у них венгерская нотация для самих локальных переменных и членов?

ГВ>>Стиль внешнего оформления задан, внутри пиши как хочешь или уточни у тех, кто дал задание.
ОК>Какой-то хреновый стиль но пусть будет по-твоему. Почему не описан внутренний стиль?

См. выше.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[13]: Тестовое задание ...
От: Selavi  
Дата: 15.06.15 07:51
Оценка:
Здравствуйте, Геннадий Васильев, Вы писали:

ГВ>Так и пяти минут достаточно, чтобы понять, что кандидат читает написанное с пятого на десятое. Я не знаю, кому как, но мне бы в ум не встало связываться с инженером, неспособным буквально последовать заданию. Какой смысл вести дальнейший разговор, если ключевой инженерный навык отсутствует?


Как же вы легко лепите ярлыки)
Давайте применим эту тактику к вам, как к работодателю или интервьюеру.

1) Вы делаете вывод о целом на основе его небольшой части. Т.е. если кандидат в точности не последовал заданию (тестовому, Карл!!!), то он сразу же "неспособен" и у него "отсутствует".

2) Cпособность кандидата самостоятельно мыслить для вас является минусом. Главное, чтобы было "точно как в ТЗ". Одной из лучших практик написания кода является взаимодействие заказчика-технолога-программиста. В вашем варианте программисту вообще не нужно думать о задаче. Нужно лишь следовать тз. В итоге задача конечно будет решена, но вместо 3х голов будут использованы только 2, причем не самые компетентные.

3) Вы смешиваете тестовое задание и реальную разработку. В большинстве случае ТЗ пишут спустя рукава, поскольку никому не хочется тратить кучу времени и сил на людей, которые оценят ваш труд поверхностным взглядом за 5 мин и в большинстве случаев вынесут решение на основе своего настроения и личных предубеждений. Но Вы об этом не думаете, ставите двойку и кричите — "Следующий!". Знаете в чем проблема? В том, что слишком много голодных кадров и слишком легко их перебирать. Вероятно ситуация в ближайшие годы изменится.

Какой же ярлык можно Вам налепить в этой ситуации?

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

з.ы.
Стоит закрыть тему. Дальше пойдут личности(
Отредактировано 15.06.2015 7:53 DvaSL . Предыдущая версия .
Re[2]: Тестовое задание ...
От: Handie  
Дата: 15.06.15 10:45
Оценка:
S>1) Сложно? Имо, наоборот, это как раз суть ООП — создавать абстракции и раскидывать их по классам. Если взять класс CThreadPoolX и просмотреть его методы, то становится однозначно понятно, что он делает. Я много раз видел, как люди запихивают все в 1 класс и это кошмар для понимания

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

Если инженер на простой задаче разводит многоклассовый ООП с мегаклассами, то на сложной задаче его накроет полный ... сложности. Мне нравится KISS — если задача может быть решена 20 строчками, не надо писать 2000
Re[8]: Тестовое задание ...
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 15.06.15 11:15
Оценка:
Здравствуйте, Selavi, Вы писали:

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


G>>И это no hire. Неважно что нравится тебе, важно что понравится заказчику. То что ты сделал — не понравилось. Тебе кучу причин уже привели почему оно могло не понравится.

S>т.е. это no hire не потому, что я плохо пишу код, а потому что я не уточнил у заказчика детали?) Что ж, у меня еще есть надежда)
Я не знаю что они подумали, я лишь отметил слабую сторону.

G>>Нет, я тупо копирую готовые рабочие решения, а потом допиливаю детали.

S>А если готового решения нет?
Значит есть близкое.

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

S>В моей реализации потоки не простаивают и выполняют задания клиентов. Плюс минимизируется время ожидания. Что в этом плохого?
У тебя постоянно курит поток, который раскидывает задачи

G>>Такая фраза не делает тебя специалистом. Кто мешал сразу уточнить детали и хотя бы API сделать ровно как написано?

S>Я уже писал об этом. ТЗ было получено через КА и мне совершенно не хотелось играть в испорченный телефон, поэтому я сам придумал вариант. В реальной обстановке, конечно же, я бы долбил заказчика до тех пор, пока ТЗ(техническое задание, не тестовое) не стало бы однозначным, что, собственно, я и делаю постоянно на текущей работе. Мне не понятно почему из этого момента раздувается такая буча.
А чем КА помешало тебе это сделать? Они же только письма форвардят и не более того. А буча раздулась потому что это критичный момент.


G>>Собеседование не для того, чтобы оценивать твое творчество. Зачем жаловаться?

S>Собеседование нужно для оценки кандидата работодателем и работодателя кандидатом. В то числе через их творчество.
Это чушь. Платят тебе не за творчество и не оценивают его.

G>>Читал, поэтому и спрашиваю. Ты хотел показать себя, а не получить работу. Показал, но работодателю оказалось все равно. Зачем жаловаться?

S>Опять эта ерунда про жалобу. Я сделал ТЗ и не получил фидбека. Это некрасиво со стороны работодателя и я написал об этом, чтобы предостеречь других.
Они тебе ничего не должны.
Re[14]: Тестовое задание ...
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 15.06.15 11:23
Оценка:
Здравствуйте, Selavi, Вы писали:

ГВ>>Так и пяти минут достаточно, чтобы понять, что кандидат читает написанное с пятого на десятое. Я не знаю, кому как, но мне бы в ум не встало связываться с инженером, неспособным буквально последовать заданию. Какой смысл вести дальнейший разговор, если ключевой инженерный навык отсутствует?


S>Как же вы легко лепите ярлыки)


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

Не принимай близко к сердцу, прямо к тебе это не относится и на самом деле отклонения от ТЗ не сопровождаются развешиванием таких ярлыков. Меня просто Олег немало рассмешил, вот я и расслабился. Блин, ну надо же такое ляпнуть
Автор: Олег К.
Дата: 15.06.15
: "Где в задании было написано, что отклоняться от интерфейса нельзя?" Гомгардия!

S>Давайте применим эту тактику к вам, как к работодателю или интервьюеру.


Хорошая мысль.

S>1) Вы делаете вывод о целом на основе его небольшой части. Т.е. если кандидат в точности не последовал заданию (тестовому, Карл!!!), то он сразу же "неспособен" и у него "отсутствует".


Выводя я делаю на основании нескольких факторов. Точность следования заданию — один из них. Его вес не абсолютный, он играет свою роль только вместе с другими оценками. Собственно, я тебе это прямо и написал
Автор: Геннадий Васильев
Дата: 13.06.15
.

S>2) Cпособность кандидата самостоятельно мыслить для вас является минусом.


С чего это вдруг?

S>Главное, чтобы было "точно как в ТЗ". Одной из лучших практик написания кода является взаимодействие заказчика-технолога-программиста. В вашем варианте программисту вообще не нужно думать о задаче. Нужно лишь следовать тз. В итоге задача конечно будет решена, но вместо 3х голов будут использованы только 2, причем не самые компетентные.


Погоди. Здесь по условиям задачи есть только двое: заказчик (я) и исполнитель (кандидат). Между нами — текст задания. В идеале кандидат сам додумает недостающие аспекты задания и сделает это так, чтобы не нарушать то, что обозначено явно. Если не получается, он вполне может прямо спросить или уточнить. Собственно, примером из твоего задания служит Init и недостающий, парный ему метод Shutdown.

S>3) Вы смешиваете тестовое задание и реальную разработку.


Тест даётся для того, чтобы составить некоторое представление о том, как человек может работать. На 100% его, конечно, переносить нельзя, но некоторое более или менее предметное впечатление о кандидате составить можно.

S>В большинстве случае ТЗ пишут спустя рукава, поскольку никому не хочется тратить кучу времени и сил на людей, которые оценят ваш труд поверхностным взглядом за 5 мин и в большинстве случаев вынесут решение на основе своего настроения и личных предубеждений. Но Вы об этом не думаете, ставите двойку и кричите — "Следующий!". Знаете в чем проблема? В том, что слишком много голодных кадров и слишком легко их перебирать. Вероятно ситуация в ближайшие годы изменится.


Вот сейчас ты сам смешиваешь тестовое задание и реальную разработку, пытаясь прямо переносить не самые удачные "реальные практики" на тест. Не надо этого делать, поскольку тест — это своего рода игра, и игра со вполне определёнными правилами.

S>Какой же ярлык можно Вам налепить в этой ситуации?


S>В голову приходит обидное слово, но я не буду его говорить, поскольку Вы единственный, кто дал полезную инфу по самому коду, за что Вам спасибо еще раз.


Пожалуйста. И извини ещё раз, за то, что я тебя задел.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re: Тестовое задание ...
От: GreenTea  
Дата: 15.06.15 18:08
Оценка:
Здравствуйте, Selavi, Вы писали:

S>Заранее спасибо за потраченное время.


Привет. Реализацию не смотрел, просто решил выполнить задание на java (мой родной язык). Получилось так:

package net.sf.brunneng;


import java.util.*;

public class ThreadPool {

   private class ClientTask {
      final int clientId;
      final Runnable runnable;

      public ClientTask(int clientId, Runnable runnable) {
         this.clientId = clientId;
         this.runnable = runnable;
      }
   }

   private boolean closed;
   private final List<ClientTask> tasks = Collections.synchronizedList(new ArrayList<ClientTask>());

   private List<Thread> threads = new ArrayList<Thread>();
   private Set<Integer> executingClients = Collections.synchronizedSet(new HashSet<Integer>());

   public List<ClientTask> getTasks() {
      return tasks;
   }

   public ThreadPool(int threadsCount) {
      for (int i = 0; i < threadsCount; ++i) {
         Thread thread = createThread();
         threads.add(thread);
         thread.start();
      }
   }

   private ClientTask getNextClientTask() {
      ClientTask res = null;
      synchronized (tasks) {
         for (int i = 0; i < tasks.size(); i++) {
            ClientTask task = tasks.get(i);
            if (!executingClients.contains(task.clientId)) {
               res = task;
               executingClients.add(task.clientId);
               tasks.remove(i);
               break;
            }
         }
      }

      return res;
   }

   private Thread createThread() {
      return new Thread(new Runnable() {
         @Override
         public void run() {
            boolean finish = false;

            while (!finish && !closed) {
               try {
                  ClientTask task = getNextClientTask();

                  if (task == null) {
                     synchronized (tasks) {
                        tasks.wait();
                     }
                     continue;
                  }

                  task.runnable.run();
                  executingClients.remove(task.clientId);

               } catch (InterruptedException e) {
                  e.printStackTrace();
                  finish = true;
               }
            }
         }
      });
   }

   public void addTask(int clientId, Runnable task) {
      tasks.add(new ClientTask(clientId, task));

      if (!executingClients.contains(clientId)) {
         synchronized (tasks) {
            tasks.notify();
         }
      }
   }

   public void close() {
      closed = true;

      synchronized (tasks) {
         tasks.notifyAll();
      }

      for (Thread thread : threads) {
         try {
            thread.join();
         } catch (InterruptedException ignored) {

         }
      }
   }
}


Если кто шарит в java, прошу поревьювить. Я сам с ручной многопоточностью редко работал, так что могут быть косяки..
Re[2]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 15.06.15 18:32
Оценка:
Здравствуйте, GreenTea, Вы писали:

GT>Если кто шарит в java, прошу поревьювить. Я сам с ручной многопоточностью редко работал, так что могут быть косяки..


Представь ситуацию, когда в первой половине tasks у нас задачи от первого клиента, а во второй — от второго, и всего один рабочий поток. Он будет выполнять все задания по списку, то есть сначала все от первого клиента, хотя по заданию должен чередовать.
Re[15]: Тестовое задание ...
От: Selavi  
Дата: 15.06.15 19:09
Оценка:
ГВ>Пожалуйста. И извини ещё раз, за то, что я тебя задел.

Нет проблем. С Вами приятно общаться)
Re[3]: Тестовое задание ...
От: GreenTea  
Дата: 15.06.15 19:27
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

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


GT>>Если кто шарит в java, прошу поревьювить. Я сам с ручной многопоточностью редко работал, так что могут быть косяки..


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


Согласен.. Поправил.

package net.sf.brunneng;


import java.util.*;

public class ThreadPool {

   private class ClientTask {
      final int clientId;
      final Runnable runnable;

      public ClientTask(int clientId, Runnable runnable) {
         this.clientId = clientId;
         this.runnable = runnable;
      }
   }

   private boolean closed;
   private final List<ClientTask> tasks = Collections.synchronizedList(new ArrayList<ClientTask>());

   private List<Thread> threads = new ArrayList<Thread>();
   private Set<Integer> executingClients = Collections.synchronizedSet(new HashSet<Integer>());

   public List<ClientTask> getTasks() {
      return tasks;
   }

   public ThreadPool(int threadsCount) {
      for (int i = 0; i < threadsCount; ++i) {
         Thread thread = createThread();
         threads.add(thread);
         thread.start();
      }
   }

   private ClientTask getNextClientTask() {
      ClientTask res = null;
      synchronized (tasks) {
         for (int i = 0; i < tasks.size(); i++) {
            ClientTask task = tasks.get(i);
            if (!executingClients.contains(task.clientId)) {
               res = task;
               executingClients.add(task.clientId);
               tasks.remove(i);
               break;
            }
         }
      }

      return res;
   }

   private Thread createThread() {
      return new Thread(new Runnable() {
         @Override
         public void run() {
            boolean finish = false;

            while (!finish && !closed) {
               try {
                  ClientTask task = getNextClientTask();

                  if (task == null) {
                     synchronized (tasks) {
                        tasks.wait();
                     }
                     continue;
                  }

                  task.runnable.run();
                  executingClients.remove(task.clientId);

               } catch (InterruptedException e) {
                  e.printStackTrace();
                  finish = true;
               }
            }
         }
      });
   }

   public void addTask(int clientId, Runnable task) {
      synchronized (tasks) {
         int nextIndex = findBestIndexToInsert(clientId);
         tasks.add(nextIndex, new ClientTask(clientId, task));

         if (!executingClients.contains(clientId)) {
            tasks.notify();
         }
      }
   }

   private int findBestIndexToInsert(int clientId) {
      int minIndex = 0;

      for (int i = 0; i < tasks.size(); ++i) {
         ClientTask task = tasks.get(i);
         if (task.clientId == clientId) {
            minIndex = i + 1;
         }
      }

      return minIndex < tasks.size() ? minIndex + 1 : minIndex;
   }

   public void close() {
      closed = true;

      synchronized (tasks) {
         tasks.notifyAll();
      }

      for (Thread thread : threads) {
         try {
            thread.join();
         } catch (InterruptedException ignored) {

         }
      }
   }
}
Отредактировано 15.06.2015 19:31 GreenTea . Предыдущая версия .
Re[4]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 15.06.15 19:39
Оценка:
Здравствуйте, GreenTea, Вы писали:

GT>Согласен.. Поправил.

GT>
GT>   public void addTask(int clientId, Runnable task) {
GT>      synchronized (tasks) {
GT>         int nextIndex = findBestIndexToInsert(clientId);
GT>         tasks.add(nextIndex, new ClientTask(clientId, task));

GT>         if (!executingClients.contains(clientId)) {
GT>            tasks.notify();
GT>         }
GT>      }
GT>   }

GT>   private int findBestIndexToInsert(int clientId) {
GT>      int minIndex = 0;

GT>      for (int i = 0; i < tasks.size(); ++i) {
GT>         ClientTask task = tasks.get(i);
GT>         if (task.clientId == clientId) {
GT>            minIndex = i + 1;
GT>         }
GT>      }
GT>



Сложность N вызовов addTask квадратичная — O(N*N).
Re[5]: Тестовое задание ...
От: GreenTea  
Дата: 15.06.15 19:52
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

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


GT>>Согласен.. Поправил.

GT>>
GT>>   public void addTask(int clientId, Runnable task) {
GT>>      synchronized (tasks) {
GT>>         int nextIndex = findBestIndexToInsert(clientId);
GT>>         tasks.add(nextIndex, new ClientTask(clientId, task));

GT>>         if (!executingClients.contains(clientId)) {
GT>>            tasks.notify();
GT>>         }
GT>>      }
GT>>   }

GT>>   private int findBestIndexToInsert(int clientId) {
GT>>      int minIndex = 0;

GT>>      for (int i = 0; i < tasks.size(); ++i) {
GT>>         ClientTask task = tasks.get(i);
GT>>         if (task.clientId == clientId) {
GT>>            minIndex = i + 1;
GT>>         }
GT>>      }
GT>>



EP>Сложность N вызовов addTask квадратичная — O(N*N).


Ну и что? Даже при миллионе тасок этот цикл будет выполняться за миллисекунды. А ситуация что там будет миллион тасок врят-ли реальна.
Re[6]: Тестовое задание ...
От: GreenTea  
Дата: 15.06.15 20:02
Оценка:
EP>>Сложность N вызовов addTask квадратичная — O(N*N).

GT>Ну и что? Даже при миллионе тасок этот цикл будет выполняться за миллисекунды. А ситуация что там будет миллион тасок врят-ли реальна.


К слову только что замерил, вот такой цикл
       long start = System.currentTimeMillis();

       long count = 0;
       for (int i = 0; i < 10000000; ++i) {
          if (args.length % (i+1) == 0) {
             count++;
          }
       }
       long end = System.currentTimeMillis();
       System.out.println("Count = " + count + "; execution time: "  + (end - start) + "ms");


Выводит у меня:
Count = 10000000; execution time: 30ms
Re[6]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 15.06.15 20:23
Оценка:
Здравствуйте, GreenTea, Вы писали:

GT>Ну и что?


Premature pessimization. O(N*N) вместо O(N), или хотя бы O(N * ln(N)).

GT>Даже при миллионе тасок этот цикл будет выполняться за миллисекунды.


На добавление миллиона заданий потребуется 500 миллиардов итераций — это как минимум минуты под мьютексом. Для десяти миллионов — уже 50 триллионов, а это уже часы.

GT>А ситуация что там будет миллион тасок врят-ли реальна.


Почему? Это же не потоки.
Re[7]: Тестовое задание ...
От: GreenTea  
Дата: 15.06.15 20:36
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

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


GT>>Ну и что?


EP>Premature pessimization. O(N*N) вместо O(N), или хотя бы O(N * ln(N)).


GT>>Даже при миллионе тасок этот цикл будет выполняться за миллисекунды.


EP>На добавление миллиона заданий потребуется 500 миллиардов итераций — это как минимум минуты под мьютексом. Для десяти миллионов — уже 50 триллионов, а это уже часы.


GT>>А ситуация что там будет миллион тасок врят-ли реальна.


EP>Почему? Это же не потоки.


Ну так таски еще будут выполняться параллельно. Поэтому очередь будет очищаться. А если взять и допустить что за долисекунды придут триллионы тасок, тогда по памяти все умрет. Но зачем вдаваться в такие крайности.
Re[3]: Тестовое задание ...
От: VladFein США  
Дата: 15.06.15 20:45
Оценка:
Здравствуйте, antropolog, Вы писали:

A>А надо было всего-лишь реализовать простой паттерн — привязку продюсера к конкретному потоку. В данном случае это делается отдельной очередью на каждый поток, и помещением таски в очередь с индексом = taskID % threadNum


Такого требования нет в задании.
Таск от любого клиента может выполняться любым потоком, если только другой поток не выполняет в это время таск этого клиента.
Мне видится простой алгоритм: все таски (с ID клиента) ставятся в одну очередь, в порядке поступления.
CThreadPool имеет set "клиентов в процессе".
Потокам при создании передаётся callback на:
CTask* CThreadPool::NextTask(int& _clientId);
Где [in, out] _clientId — при вызове равен ID предыдущего клиента (например, -1 для начала), а по возвращении — текущего.
Потоки работают в бесконечном цикле NextTask() / Execute(). Ведь конечного условия не дано?

CThreadPool::NextTask() удаляет ID предыдущего клиента из set'а, перебирает лист в поисках первого ID клиента, не найденного в том set'е, добавляет его в set, удаляет из очереди и возвращает потоку.

В задании не говорится об ответственности за удаление CTask; очень может быть, что его Execute() делает delete this; перед возвращением.

Итого: 3-4 строки на функцию потока, 2-3 — на AddTask() и 15-20 на NextTask().

@ТС: к сожалению, ссылка у меня на работе не открывается (по соображениям безопасности).
Re[8]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 15.06.15 20:50
Оценка:
Здравствуйте, GreenTea, Вы писали:

EP>>Почему? Это же не потоки.

GT>Ну так таски еще будут выполняться параллельно. Поэтому очередь будет очищаться. А если взять и допустить что за долисекунды придут триллионы тасок, тогда по памяти все умрет. Но зачем вдаваться в такие крайности.

Даже если закрыть глаза на квадратичность — там всё равно условие не выполняется. Возможны вот такие последовательные состояния списка заданий (индексы клиентов):
1 2 3 4 5
add(7)
1 7 2 3 4 5
process(1)
7 2 3 4 5
add(1)
7 1 2 3 4 5
process(7)
1 2 3 4 5
Получаем цикл.
Re[4]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 15.06.15 21:03
Оценка:
Здравствуйте, VladFein, Вы писали:

VF>Такого требования нет в задании.

VF>Таск от любого клиента может выполняться любым потоком, если только другой поток не выполняет в это время таск этого клиента.
VF>Мне видится простой алгоритм: все таски (с ID клиента) ставятся в одну очередь, в порядке поступления.
VF>CThreadPool имеет set "клиентов в процессе".
VF>Потокам при создании передаётся callback на:
VF>CTask* CThreadPool::NextTask(int& _clientId);
VF>Где [in, out] _clientId — при вызове равен ID предыдущего клиента (например, -1 для начала), а по возвращении — текущего.
VF>Потоки работают в бесконечном цикле NextTask() / Execute(). Ведь конечного условия не дано?
VF>CThreadPool::NextTask() удаляет ID предыдущего клиента из set'а, перебирает лист в поисках первого ID клиента, не найденного в том set'е, добавляет его в set, удаляет из очереди и возвращает потоку.

В этой схеме условие тоже не выполняется. Пример списка заданий (индексы клиентов):
1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 3 4 5 6 7
Отредактировано 15.06.2015 21:05 Evgeny.Panasyuk . Предыдущая версия .
Re[9]: Тестовое задание ...
От: GreenTea  
Дата: 15.06.15 21:04
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

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


EP>>>Почему? Это же не потоки.

GT>>Ну так таски еще будут выполняться параллельно. Поэтому очередь будет очищаться. А если взять и допустить что за долисекунды придут триллионы тасок, тогда по памяти все умрет. Но зачем вдаваться в такие крайности.

EP>Даже если закрыть глаза на квадратичность — там всё равно условие не выполняется. Возможны вот такие последовательные состояния списка заданий (индексы клиентов):

EP>
EP>1 2 3 4 5
EP>add(7)
EP>1 7 2 3 4 5
EP>process(1)
EP>7 2 3 4 5
EP>add(1)
EP>7 1 2 3 4 5
EP>process(7)
EP>1 2 3 4 5
EP>
Получаем цикл.


Но это частный случай в котором случайно совпали тайминги добавления и обработки тасок. В общем же случае вероятность такой ситуации очень мала.
Отредактировано 15.06.2015 21:04 GreenTea . Предыдущая версия .
Re[10]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 15.06.15 21:14
Оценка:
Здравствуйте, GreenTea, Вы писали:

GT>Но это частный случай в котором случайно совпали тайминги добавления и обработки тасок. В общем же случае вероятность такой ситуации очень мала.


Даже если и так — условие всё равно не выполняется. Я бы ещё понял если бы это условие было трудно достичь — так ведь нет, есть же простой вариант циклического перебора клиентов.
Re[5]: Тестовое задание ...
От: VladFein США  
Дата: 15.06.15 21:15
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

  Скрытый текст
VF>>Такого требования нет в задании.
VF>>Таск от любого клиента может выполняться любым потоком, если только другой поток не выполняет в это время таск этого клиента.
VF>>Мне видится простой алгоритм: все таски (с ID клиента) ставятся в одну очередь, в порядке поступления.
VF>>CThreadPool имеет set "клиентов в процессе".
VF>>Потокам при создании передаётся callback на:
VF>>CTask* CThreadPool::NextTask(int& _clientId);
VF>>Где [in, out] _clientId — при вызове равен ID предыдущего клиента (например, -1 для начала), а по возвращении — текущего.
VF>>Потоки работают в бесконечном цикле NextTask() / Execute(). Ведь конечного условия не дано?
VF>>CThreadPool::NextTask() удаляет ID предыдущего клиента из set'а, перебирает лист в поисках первого ID клиента, не найденного в том set'е, добавляет его в set, удаляет из очереди и возвращает потоку.

EP>В этой схеме условие тоже не выполняется. Пример списка заданий (индексы клиентов):
EP>
EP>1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 3 4 5 6 7
EP>

Вы об этом?

Не должно быть "повисших" клиентов, даже если клиентов больше, чем потоков.

Мы, похоже, по разному читаем это условие.
Я понял, что нельзя "без очереди" обрабатывать клиента #N только потому, что какой-то поток уже имел с ним дело.
Иначе для чего же подсказка "даже если клиентов больше, чем потоков"?
Другими словами — никакой thread affinity.
Re[6]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 15.06.15 21:20
Оценка:
Здравствуйте, VladFein, Вы писали:

EP>>В этой схеме условие тоже не выполняется. Пример списка заданий (индексы клиентов):

EP>>
EP>>1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 3 4 5 6 7
EP>>

VF>Вы об этом?

Не должно быть "повисших" клиентов, даже если клиентов больше, чем потоков.

VF>Мы, похоже, по разному читаем это условие.
VF>Я понял, что нельзя "без очереди" обрабатывать клиента #N только потому, что какой-то поток уже имел с ним дело.
VF>Иначе для чего же подсказка "даже если клиентов больше, чем потоков"?
VF>Другими словами — никакой thread affinity.

Там сказано:

Т.е. поток должен обрабатывать разных клиентов по-очереди, а не одного до завершения всех его задач.

В этой же схеме обрабатываются задачи клиентов 1 и 2 до завершения всех их задач, а клиенты 3, 4, 5, 6, 7 — всё это время ждут.
Re[7]: Тестовое задание ...
От: VladFein США  
Дата: 15.06.15 21:24
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Там сказано:

EP>

EP>Т.е. поток должен обрабатывать разных клиентов по-очереди, а не одного до завершения всех его задач.

EP>В этой же схеме обрабатываются задачи клиентов 1 и 2 до завершения всех их задач, а клиенты 3, 4, 5, 6, 7 — всё это время ждут.

Опять зе — "по очереди", т.е. не давая преимущества клиентам по принципу affinity (или другим). Очередь-то я соблюдаю?
Re[8]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 15.06.15 21:30
Оценка:
Здравствуйте, VladFein, Вы писали:

EP>>Там сказано:

EP>>

EP>>Т.е. поток должен обрабатывать разных клиентов по-очереди, а не одного до завершения всех его задач.

EP>>В этой же схеме обрабатываются задачи клиентов 1 и 2 до завершения всех их задач, а клиенты 3, 4, 5, 6, 7 — всё это время ждут.
VF>Опять зе — "по очереди", т.е. не давая преимущества клиентам по принципу affinity (или другим).

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

VF>Очередь-то я соблюдаю?


Какую?
Re[9]: Тестовое задание ...
От: VladFein США  
Дата: 15.06.15 21:48
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

VF>>Очередь-то я соблюдаю?


EP>Какую?


Живую? В порядке поступления, так сказать.
Я понял так, что поток А не должен вытягивать клиента Б "одного до завершения всех его задач", а должен брать из очереди, следя что бы "Задачи одного клиента обрабатываются в том порядке, в котором они были добавлены" (а НЕ параллельно).
Клиент, не подавший задания, не может считаться "повисшим".
Я допускаю, что Ваше прочтения задания верно; это я бы уточнил. Но тогда какой смысл в уточнении "даже если клиентов больше, чем потоков"?
Re[10]: Тестовое задание ...
От: Selavi  
Дата: 15.06.15 21:59
Оценка:
Здравствуйте, VladFein, Вы писали:

VF>Здравствуйте, Evgeny.Panasyuk, Вы писали:


VF>>>Очередь-то я соблюдаю?


EP>>Какую?


VF>Живую? В порядке поступления, так сказать.

VF>Я понял так, что поток А не должен вытягивать клиента Б "одного до завершения всех его задач", а должен брать из очереди, следя что бы "Задачи одного клиента обрабатываются в том порядке, в котором они были добавлены" (а НЕ параллельно).
VF>Клиент, не подавший задания, не может считаться "повисшим".
VF>Я допускаю, что Ваше прочтения задания верно; это я бы уточнил. Но тогда какой смысл в уточнении "даже если клиентов больше, чем потоков"?

Заметьте, что моей реализации, которую все обос*али вышеописанных проблем нет)
Re[10]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 15.06.15 22:06
Оценка:
Здравствуйте, VladFein, Вы писали:

VF>Я допускаю, что Ваше прочтения задания верно; это я бы уточнил. Но тогда какой смысл в уточнении "даже если клиентов больше, чем потоков"?


Задачи одного клиента обрабатываются последовательно. Если клиентов не больше чем потоков — то тут трудно что-то напутать, параллельно будут обрабатываться задачи всех клиентов.
Если же клиентов больше, то при неправильной схеме возможно неравномерное распределение клиентских задач по потокам. Например клиенты 3, 4, 5, 6, 7 ждут пока не выполнятся ВСЕ задачи клиентов 1 и 2.
Re[11]: Тестовое задание ...
От: GreenTea  
Дата: 15.06.15 22:10
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

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


GT>>Но это частный случай в котором случайно совпали тайминги добавления и обработки тасок. В общем же случае вероятность такой ситуации очень мала.


EP>Даже если и так — условие всё равно не выполняется. Я бы ещё понял если бы это условие было трудно достичь — так ведь нет, есть же простой вариант циклического перебора клиентов.


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

package net.sf.brunneng;


import java.util.*;

public class ThreadPool {

   private class ClientTask {
      final int clientId;
      final Runnable runnable;

      public ClientTask(int clientId, Runnable runnable) {
         this.clientId = clientId;
         this.runnable = runnable;
      }
   }

   private static final int MAX_EXECUTED_CLIENTS_HISTORY = 100;

   private boolean closed;
   private final List<ClientTask> tasks = Collections.synchronizedList(new ArrayList<ClientTask>());

   private List<Thread> threads = new ArrayList<Thread>();
   private Set<Integer> executingClients = Collections.synchronizedSet(new HashSet<Integer>());

   private LinkedList<Integer> executedClientsHistory = new LinkedList<Integer>();

   public List<ClientTask> getTasks() {
      return tasks;
   }

   public ThreadPool(int threadsCount) {
      for (int i = 0; i < threadsCount; ++i) {
         Thread thread = createThread();
         threads.add(thread);
         thread.start();
      }
   }

   private ClientTask getNextClientTask() {
      ClientTask res = null;

      synchronized (tasks) {

         Map<Integer, Integer> clientIdToEarliestTaskInstanceMap = new HashMap<Integer, Integer>();
         for (int i = 0; i < tasks.size(); i++) {
            ClientTask task = tasks.get(i);
            if (!executingClients.contains(task.clientId) &&
                    !clientIdToEarliestTaskInstanceMap.containsKey(task.clientId)) {
               clientIdToEarliestTaskInstanceMap.put(task.clientId, i);
            }
         }

         final Map<Integer, Integer> clientIdMinStepsBack = new HashMap<Integer, Integer>();
         Iterator<Integer> iterator = executedClientsHistory.descendingIterator();
         int i = 1;
         while (iterator.hasNext()) {
            Integer clientId = iterator.next();
            if (!clientIdMinStepsBack.containsKey(clientId)) {
               clientIdMinStepsBack.put(clientId, i);
            }
            i++;
         }

         int maxStepBack = -1;
         Integer bestEarliestTaskIndex = null;
         for (Integer clientId : clientIdToEarliestTaskInstanceMap.keySet()) {
            Integer stepsBack = clientIdMinStepsBack.get(clientId);
            if (stepsBack == null) { // no occurence in history
               bestEarliestTaskIndex = clientIdToEarliestTaskInstanceMap.get(clientId);
               break;
            }
            else if (stepsBack > maxStepBack) {
               maxStepBack = stepsBack;
               bestEarliestTaskIndex = clientIdToEarliestTaskInstanceMap.get(clientId);
            }
         }

         if (bestEarliestTaskIndex != null) {
            res = tasks.remove(bestEarliestTaskIndex.intValue());
            executingClients.add(res.clientId);

            executedClientsHistory.add(res.clientId);
            if (executedClientsHistory.size() > MAX_EXECUTED_CLIENTS_HISTORY) {
               executedClientsHistory.removeFirst();
            }
         }
      }

      return res;
   }

   private Thread createThread() {
      return new Thread(new Runnable() {
         @Override
         public void run() {
            boolean finish = false;

            while (!finish && !closed) {
               try {
                  ClientTask task = getNextClientTask();

                  if (task == null) {
                     synchronized (tasks) {
                        tasks.wait();
                     }
                     continue;
                  }

                  task.runnable.run();
                  executingClients.remove(task.clientId);

               } catch (InterruptedException e) {
                  e.printStackTrace();
                  finish = true;
               }
            }
         }
      });
   }

   public void addTask(int clientId, Runnable task) {
      synchronized (tasks) {
         tasks.add(new ClientTask(clientId, task));

         if (!executingClients.contains(clientId)) {
            tasks.notify();
         }
      }
   }

   public void close() {
      closed = true;

      synchronized (tasks) {
         tasks.notifyAll();
      }

      for (Thread thread : threads) {
         try {
            thread.join();
         } catch (InterruptedException ignored) {

         }
      }
   }
}
Отредактировано 15.06.2015 22:22 GreenTea . Предыдущая версия .
Re[12]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 15.06.15 23:36
Оценка:
Здравствуйте, GreenTea, Вы писали:

EP>>Даже если и так — условие всё равно не выполняется. Я бы ещё понял если бы это условие было трудно достичь — так ведь нет, есть же простой вариант циклического перебора клиентов.

GT>Тогда вот так.. Тут трекается история clientId тасок которые выполнялись, в последовательности выполнения, и из свободных берется самая ранняя таская клиента, который обрабатывался раньше всех по истории.
GT>Величина истории ограничена 100, если надо можно вынести это в параметр пула.

Требуется обойти всех клиентов по порядку + зацикливание на начало, всё
Зачем так сложно-то? В getNextClientTask создаётся два отображения, крутятся три цикла, всё это под мьютексом, да ещё и введён "параметр аппроксимации решения"
Re[12]: Тестовое задание ...
От: Selavi  
Дата: 16.06.15 07:04
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

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


S>>Заметьте, что моей реализации, которую все обос*али вышеописанных проблем нет)


EP>Там есть проблемы покруче, например:

EP>* одно и то же задание может выполнится несколько раз, так как не учитываются spurious wakeup.
EP>* выполнение не заданного задания (segfault).
EP>* отсутствие join для CThreadPoolX, в результате std::terminate.

Да, Вы правы...вот черт( Наконец то хороший анализ!

А не могли бы подробней про 2 и 3?
Re[13]: Тестовое задание ...
От: GreenTea  
Дата: 16.06.15 07:54
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

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


EP>>>Даже если и так — условие всё равно не выполняется. Я бы ещё понял если бы это условие было трудно достичь — так ведь нет, есть же простой вариант циклического перебора клиентов.

GT>>Тогда вот так.. Тут трекается история clientId тасок которые выполнялись, в последовательности выполнения, и из свободных берется самая ранняя таская клиента, который обрабатывался раньше всех по истории.
GT>>Величина истории ограничена 100, если надо можно вынести это в параметр пула.

EP>Требуется обойти всех клиентов по порядку + зацикливание на начало, всё

EP>Зачем так сложно-то? В getNextClientTask создаётся два отображения, крутятся три цикла, всё это под мьютексом, да ещё и введён "параметр аппроксимации решения"

Не понял. Какое еще зацикливание? Возможно сложно, но это первое что пришло в голову. Хотелось бы посмотреть на ваше решение.
Re[14]: Тестовое задание ...
От: Слава  
Дата: 16.06.15 11:24
Оценка:
Здравствуйте, GreenTea, Вы писали:

GT>...


В этой ветке мы видим подтверждение фразы "многопоточность — это СЛОЖНО".

И тем более неясными становятся придирки по оформлению при выполненной задаче, которые ставились в вину создателю темы.
Re[15]: Тестовое задание ...
От: GreenTea  
Дата: 16.06.15 15:26
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

Да, так будет проще.
Re[3]: Тестовое задание ...
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 16.06.15 17:17
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

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


GT>>Если кто шарит в java, прошу поревьювить. Я сам с ручной многопоточностью редко работал, так что могут быть косяки..


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

Почему бы не сделать roud-robin с ид клиентами и бегать по ней выбора очередного клиента и уже из клиентской очереди выбирать таск на выполнение? Тогда ни у одного клиента не будет долгого простоя.
Sic luceat lux!
Re[14]: Тестовое задание ...
От: Selavi  
Дата: 16.06.15 18:01
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>А до этого что, плохие были?


Да, были плохие. В основном они касались оформления кода и внимательности при чтении задания. Проблем реализации многопоточного пула коснулись только Вы.

EP>Люди потратили своё время, посмотрели код, даже если тебе обидно — постарайся сдерживать эмоции.


Спасибо за совет, но лучше давайте говорить о программировании.

S>>А не могли бы подробней про 2 и 3?


EP>2. Рассмотри ситуацию когда рабочий поток создался, не получил ни одного задания, а потом разрушился. В этом случае поток проснётся и попробует выполнять this->task, который указывает в никуда.


Да, но это дело обернуто в try..catch и ничего плохого не случится. Ошибка залогируется и едем дальше. Хотя, конечно, надо добавить проверку на nullptr

EP>3. Управляющий поток CThreadPoolX создаётся, но нигде ни join'ится, ни detach'ится. В таком случае деструктор std::thread сделает std::terminate.


Как так? В деструкторе же join-ится

CThreadPoolX::~CThreadPoolX()
{
    terminated = true;
    jobCV.notify_one();
    thread.join();
}
Re[4]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 16.06.15 18:32
Оценка:
Здравствуйте, Kernan, Вы писали:

GT>>>Если кто шарит в java, прошу поревьювить. Я сам с ручной многопоточностью редко работал, так что могут быть косяки..

EP>>Представь ситуацию, когда в первой половине tasks у нас задачи от первого клиента, а во второй — от второго, и всего один рабочий поток. Он будет выполнять все задания по списку, то есть сначала все от первого клиента, хотя по заданию должен чередовать.
K>Почему бы не сделать roud-robin с ид клиентами и бегать по ней выбора очередного клиента и уже из клиентской очереди выбирать таск на выполнение? Тогда ни у одного клиента не будет долгого простоя.

Да, думаю это самое простое, я уже выше предлагал:

EP>* Каждый рабочий поток делает в цикле try_pop из очереди очередей клиентов. Если получилось достать очередь клиента — то делаем try_pop задания из его очереди, если задание есть — то выполняем его, потом очередь клиента ставится (push) в конец общей очереди.

Re[15]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 16.06.15 19:05
Оценка:
Здравствуйте, Selavi, Вы писали:

EP>>А до этого что, плохие были?

S>Да, были плохие. В основном они касались оформления кода и внимательности при чтении задания. Проблем реализации многопоточного пула коснулись только Вы.

Проблемы это же не только явные баги, но и неудачные технические решения. Например выше говорили про лишний поток, или проблемы с жизненным циклом объектов — это вполне объективные недостатки, а не "просто оформление". А не следование API заданному в ТЗ — вообще выглядит как намеренная диверсия.

S>>>А не могли бы подробней про 2 и 3?

EP>>2. Рассмотри ситуацию когда рабочий поток создался, не получил ни одного задания, а потом разрушился. В этом случае поток проснётся и попробует выполнять this->task, который указывает в никуда.
S>Да, но это дело обернуто в try..catch и ничего плохого не случится. Ошибка залогируется и едем дальше.

Это Undefined Behaviour — в лучшем случае получишь segmentation fault, в худшем — выполнение произвольного кода с последующим penetration.

S>Хотя, конечно, надо добавить проверку на nullptr


Это не поможет, там остаётся возможность выполнения последнего задания дважды (даже не считая spurious wakeup).

EP>>3. Управляющий поток CThreadPoolX создаётся, но нигде ни join'ится, ни detach'ится. В таком случае деструктор std::thread сделает std::terminate.

S>Как так? В деструкторе же join-ится
S>
S>CThreadPoolX::~CThreadPoolX()
S>{
S>    terminated = true;
S>    jobCV.notify_one();
S>    thread.join();
S>}
S>


Только что скачал этот файл заново:
CThreadPoolX::~CThreadPoolX()
{
}
Re[16]: Тестовое задание ...
От: Selavi  
Дата: 16.06.15 19:26
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Проблемы это же не только явные баги, но и неудачные технические решения. Например выше говорили про лишний поток, или проблемы с жизненным циклом объектов — это вполне объективные недостатки, а не "просто оформление". А не следование API заданному в ТЗ — вообще выглядит как намеренная диверсия.


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

EP>Это не поможет, там остаётся возможность выполнения последнего задания дважды (даже не считая spurious wakeup).


Вот последняя версия рабочего метода потока. Разве тут есть вышеописанные проблемы?

void CWorkThread::ThreadFunc()
{
    std::unique_lock<std::mutex> lock(mutex);

    while (!this->terminated)
    {
        while (!this->task)
          cv.wait(lock);

        std::lock_guard<std::mutex> lockWorkFlag(busyFlag);

        try
        {
            std::cout << "Started  Client " + std::to_string(clientID)+" in workerThread " + std::to_string((int)this) + "\r\n";
            if (this->task)
                this->task->Execute();
        }
        catch (const std::exception &e)
        {
            //Log
        }

        this->task = nullptr;

        std::cout << "Finished Client " + std::to_string(clientID) + " in workerThread " + std::to_string((int)this) + "\r\n";

        clientTaskFinisher->FinishClientTask(clientID);
    }
}



EP>Только что скачал этот файл заново:


Да, мой косяк) Я добавил код в деструктор уже после того, как выложил файл.
Re[15]: Тестовое задание ...
От: Олег К.  
Дата: 16.06.15 19:34
Оценка:
ГВ>Не принимай близко к сердцу, прямо к тебе это не относится и на самом деле отклонения от ТЗ не сопровождаются развешиванием таких ярлыков. Меня просто Олег немало рассмешил, вот я и расслабился. Блин, ну надо же такое ляпнуть
Автор: Олег К.
Дата: 15.06.15
: "Где в задании было написано, что отклоняться от интерфейса нельзя?" Гомгардия!


Где в задании написано, что нужно создавать объект этого класса? Вот тебе другой пример. Сказано что написать только один класс и все.

Ну и ты не понимаешь самой сути задания. Задание было решить такую-то задачу. А все эти интерфейсы — это второстепенное, и нужно быть совсем уж идиотом чтобы считать, что в реальной задаче ТС сделал бы по-своему. Ну и интерфейсы по работе зачастаю обсуждаются между коллегами все-таки и очень часто могут меняться в силу тех или иных обстоятельств.
Re[10]: Тестовое задание ...
От: Олег К.  
Дата: 16.06.15 19:36
Оценка:
ОК>>Почему эта ситуация не была описана в задании? Далее, почему тут можно кандидату выбирать по вкусу а изменить интерфейс как ему нравится нельзя?

S>Собственно, прямо в точку. В соседней ветке я говорил кому то, что ТЗ было получено через кадровое и мне не хотелось устраивать испорченный телефон по уточнению задания.

S>Кстати, если бы ТЗ мне дали бы на собеседовании, то я бы, однозначно, задал уточняющие вопросы по нему и скорее всего этого топика бы не было.

До них этого не доходит. Сложно им такое понять.
Re[10]: Тестовое задание ...
От: Олег К.  
Дата: 16.06.15 19:47
Оценка:
ОК>>>>Пулу при инициализации задается число рабочих потоков:
ОК>>>>bool CThreadPool::Init(int _numThreads);
ОК>>>>Почему количество потоков не задается в конструкторе?
ГВ>>>Стереотипичный ответ: потому что предполагается двухфазная инициализация.
ОК>>Ну и нафига? Уж лучше было бы эту функцию назвать что-нибудь вроде CThreadPool::StartThreads() чтобы изменить семантику. Потоки ведь нао будет еще как-то останавливать, уничтожать, ждать их завершения и т.д.

ГВ>А какая тебе разница? Задание поставлено именно так, и никак иначе. Естественным следствием из наличия Init будет появление и Shutdown.


Мне не нравится такой интерфейс. У Майкрософта тоже не замечал таких Инитов() и Шатдаунов(). (интересно, есть ли в кью-ти такое?)

ОК>>>>Что должна вернуть функция?

ГВ>>>Внезапно: true, если все в порядке и false, если не удалось создать все запланированные потоки.
ОК>>Это твои домыслы (с коими я сам также согласен), но дай я поутрирую чуточку. Это ж сколько тебе нужно создать потоков, чтобы функция вернула ложь?

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


А почему не graceful degradation? Пытаешься выделить столько ресурсов сколько можно и работаешь уже с ними. Почему этого нет в задании? Ты ответь-то на вопрос.

ОК>>>>Что делать если нельзя создать нужное количество потоков? Убивать уже созданные или оставить их так как есть? Где описание всего этого?

ГВ>>>Ответы простейшие: а) вернуть false, б) убрать созданные.
ОК>>Смотри выше. Ну и где описание оного?

ГВ>Это стереотип, которого, ИМХО, вполне достаточно для тестового задания.


Ты о чем это? Код требует точностей. Поэтому и описание должно быть точным.

ОК>>>>Почему от ТС требуются юнит-тесты а составители не желают четко описать чего хотят?

ГВ>>>Потому что не нужно быть семи пядей во лбу, чтобы ответить на поставленные вопросы.
ОК>>То есть получается, что кто-то желает многого но сам хочет сделать малого (составить нормальное задание)?

ГВ>Да нормальное оно вполне.


Я так не считаю.

ОК>>>>В каком состоянии должны быть потоки? Running или suspended? Где описание?

ГВ>>>Это можно спросить у тех, кто дал задание. Но подразумевается, что кандидат сам выберет то, что ему по вкусу.
ОК>>Почему эта ситуация не была описана в задании? Далее, почему тут можно кандидату выбирать по вкусу а изменить интерфейс как ему нравится нельзя?

ГВ>Потому что интерфейс — это та часть, с которой обычно работают другие и согласуют его отдельно от деталей реализации. Здесь как раз промоделирована ситуация, когда интерфейс предписан извне, а реализация оставлена на усмотрение разработчика.


Ты заблуждаешься. Если давать задачу, то нужно давать задачу целиком. На примере этого же пула потоков, в том же Майкрософте спустят разработчикам не только интерфейс, а также такие низкоуровневые детали типа количества потоков в пуле по умолчанию, размер стэка и т.д. Такие детали не будут отданы на откуп разработчикам. Это все прийдет сверху.

ОК>>>>Что делает подчеркивание перед параметром? Обычно такая хрень — спереди или сзади — вводится для членов. Какая у них венгерская нотация для самих локальных переменных и членов?

ГВ>>>Стиль внешнего оформления задан, внутри пиши как хочешь или уточни у тех, кто дал задание.
ОК>>Какой-то хреновый стиль но пусть будет по-твоему. Почему не описан внутренний стиль?

ГВ>См. выше.


Это тебе надо смотреть выше. Ты не понимаешь самой сути задания. Тебе не понравился его код (ок, я согласен с тобой в этом) и тебе нужно найти аргументы чтобы обосновать свою точку зрения. Вот ты и прицепился к мелочам.
Re[18]: Тестовое задание ...
От: Selavi  
Дата: 16.06.15 20:45
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>RunTask устанавливает this->task не под мьютексом — там, насколько я вижу, возможен data race при spurious wakeup:


Не должно быть data race, поскольку в вызывающем коде перед RunTask выполняется проверка IsFree, которая в свою очередь, проверяет доступность мютекса, которым владеет рабочий поток в момент выполнения задания (пусть даже в результате spurious wakeup)
Хотя...если поток проснется между проверкой и вызовом RunTask, то будет печаль..

Видимо лучше будет добавить в RunTask захват мютекса lockWorkFlag?


EP>Далее, this->terminated — это обычный bool, соответственно тут тоже data race.


Вот это я не понял. Как тут возможна гонка?


з.ы.

Если Вам удобней, то все лежит на github — https://github.com/uuFinally/MultiThreadingTest
Re[20]: Тестовое задание ...
От: Selavi  
Дата: 16.06.15 21:31
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Это уберёт data race. Только надо делать правильный захват — на github'е сейчас неправильный.


эмм...а что в нем неправильного? Захват как захват

EP>Чтения и записи в bool terminated никак не упорядоченны между потоками. Вот что говорит стандарт по этому поводу:


Как же с этим бороться? Вешать еще один мютекс в деструктор?
Re[21]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 16.06.15 22:06
Оценка:
Здравствуйте, Selavi, Вы писали:

EP>>Это уберёт data race. Только надо делать правильный захват — на github'е сейчас неправильный.

S>эмм...а что в нем неправильного? Захват как захват

Вот тут рабочий поток под мьютексом this->mutex:
    while (!this->task) // <--- under this->mutex
        cv.wait(lock);
А вот тут управляющий поток под мьютексом this->busyFlag:
    std::lock_guard<std::mutex> lockWorkFlag(busyFlag);
    this->task = task; // <--- under this->busyFlag


EP>>Чтения и записи в bool terminated никак не упорядоченны между потоками. Вот что говорит стандарт по этому поводу:

S>Как же с этим бороться? Вешать еще один мютекс в деструктор?

Мьютекс необязательно, достаточно атомарной переменной.
Re[22]: Тестовое задание ...
От: Selavi  
Дата: 16.06.15 22:28
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Вот тут рабочий поток под мьютексом this->mutex:

EP>
EP>    while (!this->task) // <--- under this->mutex
EP>        cv.wait(lock);
EP>
А вот тут управляющий поток под мьютексом this->busyFlag:

EP>
EP>    std::lock_guard<std::mutex> lockWorkFlag(busyFlag);
    this->>task = task; // <--- under this->busyFlag
EP>



Хм...получается, что мютекс busyFlag вообще не нужен и можно пользоваться только mutex для всех случаев.

Кстати, не удалось сходу нагуглить...что будет если cv.wait вызовется в момент, когда mutex не будет locked?
Re[23]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 16.06.15 22:43
Оценка:
Здравствуйте, Selavi, Вы писали:

S>Кстати, не удалось сходу нагуглить...что будет если cv.wait вызовется в момент, когда mutex не будет locked?


Precondition violation
Re[5]: Тестовое задание ...
От: Tilir Россия http://tilir.livejournal.com
Дата: 17.06.15 05:50
Оценка:
Здравствуйте, Геннадий Васильев, Вы писали:

ГВ>- Нарушен стиль оформления имён параметров (clientID вместо _clientId).


Вообще-то этот пункт соискателю в плюс. По стандарту вы не имеете права использовать в прикладном коде имена, начинающиеся с underscore -- они все зарезервированы для нужд стандартной библиотеки.
Re[6]: Тестовое задание ...
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 17.06.15 07:29
Оценка:
Здравствуйте, Tilir, Вы писали:

ГВ>>- Нарушен стиль оформления имён параметров (clientID вместо _clientId).


T>Вообще-то этот пункт соискателю в плюс. По стандарту вы не имеете права использовать в прикладном коде имена, начинающиеся с underscore -- они все зарезервированы для нужд стандартной библиотеки.


В глобальном пространстве имён. Относительно локальных, насколько я помню, ничего не сказано.

В принципе, само по себе это "отклонение" не значит почти ничего, если бы не вот такие конструкции:

void CWorkThread::RunTask(int clientID, CTask* task)
{
    this->task = task;
    this->clientID = clientID;

    cv.notify_one();
}


Мне показалось, что если бы формальные параметры были оформлены иначе, чем имена членов класса, код был бы чуть проще.

Ну и потом, это я "обострил и преувеличил" ради того, чтобы ответить Славе, а то пришлось бы уходить в совсем уж отвлечённые материи.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[5]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 17.06.15 08:55
Оценка:
Здравствуйте, antropolog, Вы писали:

A>В то время как в моём случае:

A>отдельная lock-free очередь на каждый тред
A>NextTask/AddTask работает с конкретной очередью, мьютекс не нужен, поиск в очереди не нужен, добавление/удаление таски — одна CAS операция. Единственный недостаток — теоретическая разбалансировка, я повторюсь — теоретическая, например при малом количестве клиентов, и если у них внезапно совпал id % threadnum (ну или hash(id) % threadnum ). В реальном мире такая разбалансировка практически не встречается, а если встречается то на малом количестве "долгих клиентов" и при невезучем случае.
A>Иными словами — мой алгоритм оптимален для многоих клиентов с мелкими тасками ( т.к. минимальный shared state и отсутствие блокировок).

Твой алгоритм не соответствует условию ТЗ:

S>Не должно быть "повисших" клиентов, даже если клиентов больше, чем потоков.
S>Т.е. поток должен обрабатывать разных клиентов по-очереди, а не одного до завершения всех его задач.

Возьми простой случай одного рабочего потока — никакого "разных клиентов по-очереди" у тебя нет.
Re[7]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 17.06.15 13:23
Оценка:
Здравствуйте, antropolog, Вы писали:

EP>>Твой алгоритм не соответствует условию ТЗ:

EP>>

S>>>Не должно быть "повисших" клиентов, даже если клиентов больше, чем потоков.
S>>>Т.е. поток должен обрабатывать разных клиентов по-очереди, а не одного до завершения всех его задач.

A>100% соответствие

Нет
Там ещё другое условие не выполняется — "Разные клиенты обслуживаются параллельно".
Рассмотрим ситуацию когда у нас есть два потока и два клиента — их задания должны обслуживаться параллельно. В твоём же варианте они оба могут попасть в один поток, а второй будет простаивать.

EP>>Возьми простой случай одного рабочего потока — никакого "разных клиентов по-очереди" у тебя нет.

A>возможно у нас разное понимание "по-очереди". В моём понимании по-очереди — значит в порядке очереди, т.е. в порядке добавления в очередь.

Даже если допустить что "в порядке добавления в очередь" — то о какой конкретно очереди идёт речь? Очевидно что речь не об очередях на поток как у тебя — так как это всего лишь деталь твоей реализации.
Значит речь об очереди по времени — сначала пришло задание от одного клиента, его и выполняем, потом от второго — если есть ресурсы выполняем его, потом пришло от третьего — оно должно начать выполнятся в последнюю очередь. У тебя же сначала могут начать выполнятся задания от первого (по времени) и третьего клиента, а только потом от второго, так как его задание попало в один поток с первым клиентом

A>А твоё какое?


"Разных клиентов по-очереди" — означает что для двух клиентов сначала выполняем задание первого клиента, потом второго, потом опять первого (если его задания не выполняются).
Отредактировано 17.06.2015 13:27 Evgeny.Panasyuk . Предыдущая версия . Еще …
Отредактировано 17.06.2015 13:26 Evgeny.Panasyuk . Предыдущая версия .
Re: Тестовое задание ...
От: MozgC США http://nightcoder.livejournal.com
Дата: 18.06.15 05:19
Оценка:
Вы как-то немного аггресивно реагируете в ответ на критику (о которой вы вроде бы спрашивали). Это всего-лишь тестовое задание.. расслабтесь и просто сделайте выводы
Re[2]: Тестовое задание ...
От: DvaSL  
Дата: 18.06.15 09:02
Оценка:
Здравствуйте, MozgC, Вы писали:

MC>Вы как-то немного аггресивно реагируете в ответ на критику (о которой вы вроде бы спрашивали). Это всего-лишь тестовое задание.. расслабтесь и просто сделайте выводы


Вероятно Вы правы)
Просто хотелось услышать критику технической реализации многопоточного приложения, а всю дорогу слышал что то типа "ты изменил интерфейс, значит недостоин называться программистом".
И только 2 Евгения спасли ситуацию)

Впрочем, стоило бы формулировать четче что я хочу получить
Re[8]: Тестовое задание ...
От: antropolog  
Дата: 18.06.15 09:30
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:


EP>Значит речь об очереди по времени — сначала пришло задание от одного клиента, его и выполняем, потом от второго — если есть ресурсы выполняем его, потом пришло от третьего — оно должно начать выполнятся в последнюю очередь.


Вот это поворот.

представь что у тебя два потока, два клиента, и четыре таски ( в порядке добавления через AddTask ):
1111(1) 22222222222(2) 2222222222(3) 1111(4)
(здесь цифра — id клиента, количество цифр — время выполнения таски )

итого в начале имеем:
t1 1111(1)
t2 22222222222(2)

в первый тред попадает таска(1) от первого клиента, во второй — таска(2) от второго клиента, после того как отработала первая таска(1), по твоей логике должна выполняться обязательно таска(3) ? потому что её добавили третьей? Если да, то у тебя будет вынужден простаивать поток t1, т.к. мы не можем пускать таски одного клиента в параллель, а если нет — то таска(4) начнёт выполнятся раньше таски(3), и тогда о какой очерёдности ты говоришь? Как по-твоему здесь должен действовать шедулер?
Re[3]: Тестовое задание ...
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 18.06.15 10:43
Оценка:
Здравствуйте, DvaSL, Вы писали:

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

DSL>[...]

Ну да, именно это и произошло: ты спрашивал, почему задание получило негативную оценку, вот и ответы пошли в таком же ключе. Ну и я, конечно, перегнул на счёт "раз подчёркивания не на месте, то и говорить не о чем". Хех.

DSL>И только 2 Евгения спасли ситуацию)


Этточно.

DSL>Впрочем, стоило бы формулировать четче что я хочу получить


Спросил бы сразу про реализацию, тебе и отвечали бы по-другому. Ну ничего, что ни делается — всё к лучшему.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[9]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 18.06.15 10:55
Оценка:
Здравствуйте, antropolog, Вы писали:

EP>>Значит речь об очереди по времени — сначала пришло задание от одного клиента, его и выполняем, потом от второго — если есть ресурсы выполняем его, потом пришло от третьего — оно должно начать выполнятся в последнюю очередь.

A>Вот это поворот.
A>представь что у тебя два потока, два клиента, и четыре таски ( в порядке добавления через AddTask ):
A>...
A>в первый тред попадает таска(1) от первого клиента, во второй — таска(2) от второго клиента, после того как отработала первая таска(1), по твоей логике должна выполняться обязательно таска(3) ? потому что её добавили третьей?

Конечно же нет. Свою логику я вообще-то описал:

EP>"Разных клиентов по-очереди" — означает что для двух клиентов сначала выполняем задание первого клиента, потом второго, потом опять первого (если его задания не выполняются).

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

A>Если да, то у тебя будет вынужден простаивать поток t1, т.к. мы не можем пускать таски одного клиента в параллель, а если нет — то таска(4) начнёт выполнятся раньше таски(3), и тогда о какой очерёдности ты говоришь? Как по-твоему здесь должен действовать шедулер?


Ты потерял контекст. Восстанавливаем:
Сначала ты сказал что ты понял что имеется в виду "в порядке добавления в очередь".

A>>возможно у нас разное понимание "по-очереди". В моём понимании по-очереди — значит в порядке очереди, т.е. в порядке добавления в очередь.

Естественно возникает вопрос — о какой конкретно очереди идёт речь:

EP>Даже если допустить что "в порядке добавления в очередь" — то о какой конкретно очереди идёт речь? Очевидно что речь не об очередях на поток как у тебя — так как это всего лишь деталь твоей реализации.

Вот выяснили — что это точно не очереди на поток, о них вообще ничего не было сказано в ТЗ.
Тогда о каком порядке "порядке добавления в очередь" ты говоришь? Видимо про общую временную очередь:

EP>Значит речь об очереди по времени — сначала пришло задание от одного клиента, его и выполняем, потом от второго — если есть ресурсы выполняем его, потом пришло от третьего — оно должно начать выполнятся в последнюю очередь. У тебя же сначала могут начать выполнятся задания от первого (по времени) и третьего клиента, а только потом от второго, так как его задание попало в один поток с первым клиентом

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

A>Если да, то у тебя будет вынужден простаивать поток t1

Правильно, но тогда возвращаемся в начало:

A>>возможно у нас разное понимание "по-очереди". В моём понимании по-очереди — значит в порядке очереди, т.е. в порядке добавления в очередь.

О какой очереди ты тогда говорил, если не об очереди по времени?
Re[10]: Тестовое задание ...
От: antropolog  
Дата: 18.06.15 12:51
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

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



EP>О какой очереди ты тогда говорил, если не об очереди по времени?

Да, я тут очевидно зафейлил. Осталось тебе тоже признать, что об "очередности" здесь речи идти не может, только о балансировке, и тогда можешь сразу перечитать мой ответ для VladFein
Re[11]: Тестовое задание ...
От: Evgeny.Panasyuk Россия  
Дата: 18.06.15 15:55
Оценка:
Здравствуйте, antropolog, Вы писали:

A>Осталось тебе тоже признать, что об "очередности" здесь речи идти не может, только о балансировке, и тогда можешь сразу перечитать мой ответ для VladFein


Может. Как раз об очерёдности, но только не об строгой очерёдности по времени прибытия, а той которая "разных клиентов по-очереди" — ключевое слово выделено.
Это реализуется очередью клиентов (именно клиентов, а не заданий) — каждый рабочий поток забирает (pop) одного клиента из общей очереди клиентов, и обрабатывает одно его задание, после чего ставит (push) этого клиента в конец очереди клиентов.
За счёт того что каждый клиент, для которого выполнили одно задание, ставится в конец очереди — мы и получаем необходимое "разных клиентов по-очереди".
Re[12]: Тестовое задание ...
От: DvaSL  
Дата: 18.06.15 17:59
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

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


A>>Осталось тебе тоже признать, что об "очередности" здесь речи идти не может, только о балансировке, и тогда можешь сразу перечитать мой ответ для VladFein


EP>Может. Как раз об очерёдности, но только не об строгой очерёдности по времени прибытия, а той которая "разных клиентов по-очереди" — ключевое слово выделено.

EP>Это реализуется очередью клиентов (именно клиентов, а не заданий) — каждый рабочий поток забирает (pop) одного клиента из общей очереди клиентов, и обрабатывает одно его задание, после чего ставит (push) этого клиента в конец очереди клиентов.
EP>За счёт того что каждый клиент, для которого выполнили одно задание, ставится в конец очереди — мы и получаем необходимое "разных клиентов по-очереди".

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