Хабраиндекс
376,07
16 ноября 2012 в 08:58

Windows 8: Написание многопоточных приложений для магазина Windows с помощью Intel® Threading Building Blocks tutorial

Как известно, в программном интерфейсе приложений для магазина Windows (Windows Store apps) отсутствуют многие функции работы с потоками, начиная с CreateThread() и заканчивая работой с TLS ключами. И это отличный повод перейти от параллелизма, основанного на системно-зависимых потоках к параллелизму, основанному на задачах. Данный пост излагает пошаговую инструкцию о том, как написать простейший многопоточный пример, который проходит аттестацию для магазина Windows (Windows App Certification Kit validation) и, гипотетически, может быть масштабирован до игрушек космического масштаба. А поскольку используется кроссплатформенная библиотека Intel® Threading Building Blocks (Intel® TBB, TBB, threadingbuildingblocks.org), то вычислительная часть может быть легко перенесена на другие платформы, и задача будет заключаться только в том, чтобы нарисовать новый красивый графический интерфейс.

В недавно вышедшем релизе библиотеки Intel® TBB tbb41_20121112oss, который доступен для загрузки на нашем сайте threadingbuildingblocks.org, добавлена экспериментальная поддержка приложений для магазина Windows, т.е. использование только разрешенного программного интерфейса для разработки таких приложений.
И так, что же необходимо сделать, чтобы собрать простое приложение с использоанием Intel TBB.
Для начала необходимо распаковать и откомпилировать библиотеку, поскольку этот релиз распространяется только в исходниках. Подразумевается, что есть gnu make, в командной строке для студии 2012 выполняем команду

gnumake tbb tbbmalloc target_ui=win8ui target_ui_mode=production

Со стороны библиотеки всё, переходим в студию.
Создаем новый проект «Blank App (XAML)» используя стандартный шаблон «Visual C++» ->«Windows Store». Для простоты оставим имя проекта, предложенное по умолчанию, «App1».
Теперь добавим каталог <каталог tbb>/include в свойство проекта «Additional Include Directories» и каталог с построенной библиотекой tbb.lib в «Additional Library Directories».
Затем добавим пару кнопок на основную страницу (Класс App1.MainPage). После этого XAML файл страницы будет выглядеть примерно так

<Page
    x:Class="App1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App1"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <Button Name="SR" Margin="167,262,0,406" Height="100" Width="300" 
         Content="Press to run Simple Reduction"></Button>
        <Button Name="DR" Margin="559,262,0,406" Height="100" Width="300" 
         Content="Press to run Deterministic Reduction"></Button>
    </Grid>
</Page>


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

Подключаем TBB и добавляем обработчики нажатия кнопок. Для примера возьмем алгоритмы редукции (tbb::parallel_reduce) и детерминированной редукции (tbb::parallel_deterministic_reduce) и добавим в исходный файл для основной страницы MainPage.xaml.cpp:
#include "tbb/tbb.h"

void App1::MainPage::SR_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
    int N=1000000;
    float fr = 1.0f/(float)N;
    float sum = tbb::parallel_reduce(
        tbb::blocked_range<int>(0,N), 0.0f,
        [=](const tbb::blocked_range<int>& r, float sum)->float {
            for( int i=r.begin(); i!=r.end(); ++i )
                sum += fr;
            return sum;
    },
        []( float x, float y )->float {
            return x+y;
    }
    );	
    SR->Content="Press to run Simple Reduction\nThe result is " + sum.ToString();
}

void App1::MainPage::DR_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
    int N=1000000;
    float fr = 1.0f/(float)N;
    float sum = tbb::parallel_deterministic_reduce(
        tbb::blocked_range<int>(0,N), 0.0f,
        [=](const tbb::blocked_range<int>& r, float sum)->float {
            for( int i=r.begin(); i!=r.end(); ++i )
                sum += fr;
            return sum;
    },
        []( float x, float y )->float {
            return x+y;
    }
    );	
    DR->Content="Press to run Deterministic Reduction\nThe result is " + sum.ToString();
}


И XAML файл для основной страницы будет выглядеть так
<Page
    x:Class="App1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App1"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <Button Name="SR" Margin="167,262,0,406" Height="100" Width="300" 
         Content="Press to run Simple Reduction" Click="SR_Click"></Button>
        <Button Name="DR" Margin="559,262,0,406" Height="100" Width="300" 
         Content="Press to run Deterministic Reduction" Click="DR_Click"></Button>
    </Grid>
</Page>


Добавляем библиотеки tbb.dll и tbbmalloc.dll в контейнер приложения. Для этого нужно добавить файлы в проект (через project->add existing item) и выставить свойство “Content” в «Yes». В этом случае файлы будут скопированы в контейнер (AppX) внутри приложения и могут быть подгружены как при старте приложения, так и позже.
Готово. Можно запустить симулятор и смотреть, как работает приложение:

Следующим шагом надо запустить «Windows App Cert Kit» и проверить, что приложение успешно проходит аттестацию:


Вот и всё! Простое приложение готово, теперь можно всё усложнять.

Для тех, кто заинтересовался, пробуйте:

Скачать библиотеку Intel® Threading Building Blocks (Версия с открытым исходным кодом):
threadingbuildingblocks.org
Коммерческая версия Intel® TBB (функционально не отличается):
software.intel.com/en-us/intel-tbb

Англоязычные и русскоязычные блоги об Intel® TBB
software.intel.com/en-us/tags/17207
software.intel.com/en-us/tags/17220
Ну и, конечно, наш форум,
software.intel.com/en-us/forums/intel-threading-building-blocks
+17
7242
61
vpolin 6,5

Комментарии (22)

+1
ScratchBoom, #
А что такое Platform::Object^?
+2
dtf, #
Managed-ссылки, это C++/CX.
+1
vpolin, #
Вот здесь более подробно по референсным классам.
0
Toshas, #
Здравствуйте! У меня вопрос немного не по теме, но я уже честно попробовал много разных способов получить ответ, в том числе официальные TBB licensing FAQ, Intel support mailing list, TBB forum, StackOverflow; пока безрезультатно.

Я разрабатываю приложение для компании. Приложение с закрытым кодом, но бесплатное, и не предполагает подсчет пользователей, какие-либо манипуляции с регистрацией и прочее. Существует ли вид лицензии Intel TBB, который компания могла бы приобрести, что позволило бы мне использовать TBB в моем приложении?

Все виды коммерческих лицензий, которые я видел до сих пор, упоминают per-user, или per-LAN модель, но платить $xxx за пользователя бесплатного приложения нас не устраивает, поэтому суть вопроса сводится вот к чему: правильно ли я понял коммерческую модель описанную выше, и если да, то есть ли возможность выкупить лицензию для неограниченного распространения одного программного продукта?

Спасибо!
0
mace, #
Насколько мне известно под per-user имеется ввиду не конечный пользователь системы, а программист, который использует инструмент для разработки.
0
krogozh, #
Правильно, лицензии Intel® TBB нужны для разработчиков — сколько их, столько и лицензий (принцип как с компилятором). Количество конечных пользователей приложения с TBB не ограничивается лицензией.
+2
vpolin, #
Здравствуйте,

Я не юрист и не могу давать юридические советы по использованию или лицензированию.

Но у меня есть вопрос: Вы рассматривали использование версии Intel TBB с открытым кодом? Она доступна по лицензии GPLv2 and for the runtime exception.

И как там написано, если что-то непонятно, то лучше обратиться к своему юристу («Hopefully that text is self-explanatory. If it isn't, you need to speak to your lawyer, or the Free Software Foundation.»)

--Владимир
0
ixSci, #
К чему весь этот геморрой с интелом, когда в windows есть PPL, который полностью интегрируется с WinRT async model?
0
vpolin, #
Кроссплатформенность.

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

--Владимир
0
ixSci, #
WinRT не подразумевает кроссплатформенности. Все взаимодействие с API, всё равно, будет вынесено в отдельный модуль. Так что нет, кроссплатофрменность тут роли не играет.
А судя по тому, что выше Вы написали про GPL, то за неё еще и платить придется. Точно не конкурент PPL.
0
vpolin, #
Я не писал, что WinRT подразумевает кроссплатформенность.

Я писал, что человек при разработке своего приложения для нескольких систем не будет задумываться о потоках в написании алгоритма работы, а может опираться на кроссплатформенную библиотеку, которая уже будет заботится о Win threads, WinRT или pthreads.

про то, что надо платить за GPL — совсем не понял. Кто платит за GPL лицензию?
--Владимир
+1
ixSci, #
Вы пишите статью в контексте WinRT и XAML. Весь код, который относится к вышеозначенным, в кроссплатформенном приложении будет вынесен в отдельный модуль(скорее всего). Если только выносить весь код с потоками за пределы этого модуля. Но тогда непонятна вся помпа «мы выпустили библиотеку для Windows 8». Конкретно в windows 8 Ваша библиотека имеет спорные плюсы, мягко говоря.

Про GPL: мало кто хочет делать код публичным, GPL паразитивен. А значит остается покупать, что тоже не добавляет плюсов в копилку TBB.
+1
vpolin, #
Конкретно в windows 8 Ваша библиотека имеет спорные плюсы, мягко говоря.


Win8 + портабельность = разрыв шаблона (с) пришло в IM.

Я, конечно, извиняюсь. У Вас есть опыт командной разработки приложения для нескольких платформ (операционных систем и архитектур) одновременно? Другими словами, это мнение, основанное на опыте или на эмоциях?
0
ixSci, #
Есть. Что это меняет? Вы попробуйте ответить на мои вопросы, которые я задал ранее. Попробую его переформулировать: Зачем с такой помпой рассказывать о TBB в Windows 8, когда все её преимущество заканчиваются на кроссплатформенности?

P.S. Эмоций при написании комментариев к данной статье не испытываю.
0
ixSci, #
Т.е. лично у меня, после прочтения статьи сложилась следующая картина: в Windows8 сменилось API и от старого способа работы с тредами пора отказываться. И вот тут-то интел и решил всем помочь и «запилил» TBB под Windows 8. Более того, непонятно как до этого мы жили. Но, Вы скромно умалчиваете, что есть стандарт де-факто для написания WinRT приложений на C++ — PPL(которому уже ~2 года). Я понимаю, что блог компании и всё такое. Но это всё же хабр, а не сайт интела.
+1
vpolin, #
Последние два комментария стали очень адекватные, спасибо:) До этого было больше похоже на тролление.

Данный пост это не рассказ с помпой, а именно учебное пособие, как написать или портировать приложение на новый интерфейс windows с помощью Intel TBB. Я специально поставил значок «tutorial».

А что лучше «стандарт де-факто PPL», чистые тредпулы, Intel TBB (который, в принципе, совместим с PPL) — это всё офтопик и не рассматриваются в рамках этого блога. Копья можно и в другом блоге поломать. Преимущества и возможные недостатки библиотеки я здесь тоже не рассматривал. Так же, как наверно все заметили, блог также не рассматривает публикацию приложения в самом магазине. Я видел тут, на хабре, отличный блог по этому поводу.

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

--Владимир
0
amirul, #
Вот именно что совместим лишь «в принципе».

Есть ли поддержка Windows::Foundation::IAsyncInfo? Нет? Тогда о какой поддержке Win8 приложений вообще идет речь?

Нет apartment awareness (в частности, то самое блокирование ASTA потока не было выявлено в частности потому, что TBB «все равно» какой поток блокировать, а вот ConcRT — не все равно).

Да и в принципе использовать task-based параллелизм в TBB значительно сложнее, чем в PPL (отмечу, в последнем PPL — тот который в VS2010 тоже не особо хорошо его поддерживал).

Не говоря уже о всяких create_async для тех, кто хочет не только «потреблять» асинхронные операции, но и создавать (например в библиотеке, которая дальше используется из C# или JS).

Нет, вот конкретно в части поддержки непосредственно Windows 8 совместимость не то чтобы плохая — она полностью отсутствует.
0
krogozh, #
Про GPL: мало кто хочет делать код публичным, GPL паразитивен
TBB распространяется не по простой GPL, а по лицензии
GPLv2 and for the runtime exception
Принципиальная разница в том, что код своего приложения распространять не обязательно, и можно делать коммерческие приложения с бесплатной TBB. Какие-то нюансы есть, но в общем случае можно.
0
vpolin, #
не туда ответил
0
amirul, #
#include "tbb/tbb.h"

void App1::MainPage::XX_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
  float sum = tbb::parallel_reduce(
// ...
    );	
    Xx->Content="Press to run Simple Reduction\nThe result is " + sum.ToString();
}


Я чего-то недопонимаю или в официальном блоге Intel появилась статья о параллельном программировании, в которой НЕСКОЛЬКО раз допущена «детская» ошибка. Вы же осознаете, что одна из существенных причин, по которой МС связались с реализацией полностью нового АПИ — это попытка сделать так, чтобы «дети» больше не допускали таких ошибок.

Пожалуйста, спрячьте статью в черновики и не вытаскивайте ее пока не осознаете, почему в WinRT ВСЕ «долгие» операции сделаны асинхронными. Почему concurrency::task::wait() (и прочие блокирующие операции) бросят исключение (почитайте желтенькое вот там), если кто-нибудь попытается их использовать в похожем контексте (еще одна причина использовать ConcRT/PPL вместо TBB). Что такое task_continuation_context и как он связан с ASTA (читай UI) потоками.

Не поймите меня неправильно, TBB — неплохая библиотека, но блокировать UI поток в ПРИМЕРЕ использования библиотеки от производителя самой библиотеки — это выше моего понимания. И да, за каждое «unresponsive» приложение в сторе отныне я буду винить лично Вас
0
vpolin, #
Вообще-то у меня в блоге ни одного слова про WinRT нет.:) А если Вы посмотрите на PPL'ный concurrency::parallel_reduce(), то там нет ничего желтенького. Одно дело, когда Вы уверены, что UI поток будет стоять достаточно мало времени, чтобы пользователь не успел это прочувствовать, а совсем другое, когда Вы сами останавливаете UI поток и чего-то ждете.

Проблема блокирования UI потока — общая для любой ОС. Если Вы посмотрите наши DirectX примеры в пакете, то там есть примеры как с блокированием UI потока, так и без; как с пропуском кадров, так и без. А OpenGL примеры для OS X* реализованы только без блокирования потоков, и UI написан на Object C, а бэкэнд на С++.

Если уж на то пошло, то, на мой взгляд, вторая по значимости UI проблема — это вывести адекватный прогрессбар в UI потоке. Но и эта проблема также за рамками данной статьи:)

Какие еще «детские ошибки» в моем «хелло ворлд»? Из всего представленного примера осталось придраться только к лямбда функции и к float переменной, из-за которой получается недостаточно точный результат обычной редукции. Больше там просто слов не хватит:)
--Владимир
0
amirul, #
Начну с того, что я проверил PPL-ные (не ppltasks) алгоритмы (parallel_reduce, parallel_for и пр..) и они действительно ничего знать не знают о UI потоках. Так что здесь мне ничего не остается кроме как признать себя ослом.

Дальше, сразу отвечу на простую часть. Нет, больше «детских ошибок» в коде нет. Ни во float, ни тем более в лямбдах нет ничего плохого. Точность редукции не имеет значения для семпла (да и в «реальной жизни» производительность может быть гораздо важнее дополнительной точности) — так что здесь я тоже и не думал «придираться».

Проблема блокирования UI потока действительно общая для всех ОС. Просто потому что UI имманентно последователен. Совершено очевидно, что все пользовательские события (например нажатия на клавиши, движения/клики мыши и т.п.) должны быть обработаны в точности в том порядке, в котором поступили. По большей части то же самое касается и вывода. То есть никаких проблем с однопоточностью GUI я не вижу. В смысле, проблем дизайна не вижу — проблем ИСПОЛЬЗОВАНИЯ существует множество и одна из основных это как раз блокирование того самого UI потока.

Что же «ни слова про WinRT». Ну во первых он есть у Вас прямо вот здесь — в примерах. Все эти Windows::UI::Xaml и Platform::Object — это как раз таки WinRT (последний правда — часть проекции в C++, а не часть самого WinRT, но не суть). Да и Windows 8 в качестве платформы Вы выбрали сами. И в качестве примера используется Modern app.

Ну а теперь, собственно, обратно к сути претензий.
Блокировать UI поток операцией, для ускорения которой потребовалось распараллеливание — очень плохая идея. Либо операция выполняется быстро без всякого параллелизма, либо мы таки вычисляем все параллельно и асинхронно доставляем результат обратно в UI поток. И это тем более важно, поскольку Вы не можете предсказать сколько это «достаточно мало времени». Как количество ядер, так и производительность каждого ядра — штуки достаточно разнообразные. Код может работать без заметных задержек на машине разработчика, но будучи просто перекомпилированным под ARM внезапно начнет тормозить. Или, раз уж мы говорим о реалистичности сценариев, то вряд ли Вы будете постоянно вычислять одну и ту же величину — скорее всего будете обрабатывать какие-нибудь внешние данные. И вот тут то опять таки начинается веселье. На тестовых данных все хорошо, а у пользователя — тормозит и заикается.

В общем пример должен показывать как НУЖНО решать какую-нибудь задачу (при этом сама задача может быть и полностью высосанной из пальца, как, к примеру, те же обедающие философы). В Вашем же примере показана очень опасная техника, требующая от разработчика точного знания условий работы его программы. Не только нынешних, но и будущих. И это при том, что есть множество простых способов сделать все то же самое, но безопасно и эффективно.

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