如何:使用 parallel_invoke 来执行并行操作
本示例演示如何使用 concurrency::parallel_invoke 算法以提高执行多项操作的共享的数据源的程序的性能。 由于任何操作都不修改源,因此可以直接并行执行这些操作。
示例
请考虑以下创建类型为 MyDataType 的变量的代码示例,调用函数来初始化该变量,然后对该数据执行多项长时间操作。
MyDataType data;
initialize_data(data);
lengthy_operation1(data);
lengthy_operation2(data);
lengthy_operation3(data);
如果 lengthy_operation1、lengthy_operation2 和 lengthy_operation3 函数不修改 MyDataType 变量,则可以在不进行额外修改的情况下并行执行这些函数。
下面的示例修改了上面的示例以实现并行运行。 parallel_invoke 算法并行执行每项任务并在所有任务完成后返回。
MyDataType data;
initialize_data(data);
concurrency::parallel_invoke(
[&data] { lengthy_operation1(data); },
[&data] { lengthy_operation2(data); },
[&data] { lengthy_operation3(data); }
);
以下示例从 gutenberg.org 下载 Iliad by Homer 并对此文件执行多项操作。 此示例首先按顺序执行这些操作,然后并行执行相同操作。
// parallel-word-mining.cpp
// compile with: /EHsc /MD /DUNICODE /D_AFXDLL
#define _WIN32_WINNT 0x0501
#include <afxinet.h>
#include <ppl.h>
#include <string>
#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
using namespace concurrency;
using namespace std;
// Calls the provided work function and returns the number of milliseconds
// that it takes to call that function.
template <class Function>
__int64 time_call(Function&& f)
{
__int64 begin = GetTickCount();
f();
return GetTickCount() - begin;
}
// Downloads the file at the given URL.
CString get_http_file(CInternetSession& session, const CString& url);
// Adds each word in the provided string to the provided vector of strings.
void make_word_list(const wstring& text, vector<wstring>& words);
// Finds the most common words whose length are greater than or equal to the
// provided minimum.
vector<pair<wstring, size_t>> find_common_words(const vector<wstring>& words,
size_t min_length, size_t count);
// Finds the longest sequence of words that have the same first letter.
vector<wstring> find_longest_sequence(const vector<wstring>& words);
// Finds all pairs of palindromes that appear in the provided collection
// of words.
vector<pair<wstring, wstring>> find_palindromes(const vector<wstring>& words,
size_t min_length);
int wmain()
{
// Manages the network connection.
CInternetSession session(L"Microsoft Internet Browser");
// Download 'The Iliad' from gutenberg.org.
wcout << L"Downloading 'The Iliad'..." << endl;
wstring file = get_http_file(session, L"http://www.gutenberg.org/files/6130/6130-0.txt");
wcout << endl;
// Convert the text to a list of individual words.
vector<wstring> words;
make_word_list(file, words);
// Compare the time that it takes to perform several operations on the data
// serially and in parallel.
__int64 elapsed;
vector<pair<wstring, size_t>> common_words;
vector<wstring> longest_sequence;
vector<pair<wstring, wstring>> palindromes;
wcout << L"Running serial version...";
elapsed = time_call([&] {
common_words = find_common_words(words, 5, 9);
longest_sequence = find_longest_sequence(words);
palindromes = find_palindromes(words, 5);
});
wcout << L" took " << elapsed << L" ms." << endl;
wcout << L"Running parallel version...";
elapsed = time_call([&] {
parallel_invoke(
[&] { common_words = find_common_words(words, 5, 9); },
[&] { longest_sequence = find_longest_sequence(words); },
[&] { palindromes = find_palindromes(words, 5); }
);
});
wcout << L" took " << elapsed << L" ms." << endl;
wcout << endl;
// Print results.
wcout << L"The most common words that have five or more letters are:"
<< endl;
for_each(begin(common_words), end(common_words),
[](const pair<wstring, size_t>& p) {
wcout << L" " << p.first << L" (" << p.second << L")" << endl;
});
wcout << L"The longest sequence of words that have the same first letter is:"
<< endl << L" ";
for_each(begin(longest_sequence), end(longest_sequence),
[](const wstring& s) {
wcout << s << L' ';
});
wcout << endl;
wcout << L"The following palindromes appear in the text:" << endl;
for_each(begin(palindromes), end(palindromes),
[](const pair<wstring, wstring>& p) {
wcout << L" " << p.first << L" " << p.second << endl;
});
}
// Downloads the file at the given URL.
CString get_http_file(CInternetSession& session, const CString& url)
{
CString result;
// Reads data from an HTTP server.
CHttpFile* http_file = NULL;
try
{
// Open URL.
http_file = reinterpret_cast<CHttpFile*>(session.OpenURL(url, 1));
// Read the file.
if(http_file != NULL)
{
UINT bytes_read;
do
{
char buffer[10000];
bytes_read = http_file->Read(buffer, sizeof(buffer));
result += buffer;
}
while (bytes_read > 0);
}
}
catch (CInternetException)
{
// TODO: Handle exception
}
// Clean up and return.
delete http_file;
return result;
}
// Adds each word in the provided string to the provided vector of strings.
void make_word_list(const wstring& text, vector<wstring>& words)
{
// Add continuous sequences of alphanumeric characters to the
// string vector.
wstring current_word;
for_each(begin(text), end(text), [&](wchar_t ch) {
if (!iswalnum(ch))
{
if (current_word.length() > 0)
{
words.push_back(current_word);
current_word.clear();
}
}
else
{
current_word += ch;
}
});
}
// Finds the most common words whose length are greater than or equal to the
// provided minimum.
vector<pair<wstring, size_t>> find_common_words(const vector<wstring>& words,
size_t min_length, size_t count)
{
typedef pair<wstring, size_t> pair;
// Counts the occurrences of each word.
map<wstring, size_t> counts;
for_each(begin(words), end(words), [&](const wstring& word) {
// Increment the count of words that are at least the minimum length.
if (word.length() >= min_length)
{
auto find = counts.find(word);
if (find != end(counts))
find->second++;
else
counts.insert(make_pair(word, 1));
}
});
// Copy the contents of the map to a vector and sort the vector by
// the number of occurrences of each word.
vector<pair> wordvector;
copy(begin(counts), end(counts), back_inserter(wordvector));
sort(begin(wordvector), end(wordvector), [](const pair& x, const pair& y) {
return x.second > y.second;
});
size_t size = min(wordvector.size(), count);
wordvector.erase(begin(wordvector) + size, end(wordvector));
return wordvector;
}
// Finds the longest sequence of words that have the same first letter.
vector<wstring> find_longest_sequence(const vector<wstring>& words)
{
// The current sequence of words that have the same first letter.
vector<wstring> candidate_list;
// The longest sequence of words that have the same first letter.
vector<wstring> longest_run;
for_each(begin(words), end(words), [&](const wstring& word) {
// Initialize the candidate list if it is empty.
if (candidate_list.size() == 0)
{
candidate_list.push_back(word);
}
// Add the word to the candidate sequence if the first letter
// of the word is the same as each word in the sequence.
else if (word[0] == candidate_list[0][0])
{
candidate_list.push_back(word);
}
// The initial letter has changed; reset the candidate list.
else
{
// Update the longest sequence if needed.
if (candidate_list.size() > longest_run.size())
longest_run = candidate_list;
candidate_list.clear();
candidate_list.push_back(word);
}
});
return longest_run;
}
// Finds all pairs of palindromes that appear in the provided collection
// of words.
vector<pair<wstring, wstring>> find_palindromes(const vector<wstring>& words,
size_t min_length)
{
typedef pair<wstring, wstring> pair;
vector<pair> result;
// Copy the words to a new vector object and sort that vector.
vector<wstring> wordvector;
copy(begin(words), end(words), back_inserter(wordvector));
sort(begin(wordvector), end(wordvector));
// Add each word in the original collection to the result whose palindrome
// also exists in the collection.
for_each(begin(words), end(words), [&](const wstring& word) {
if (word.length() >= min_length)
{
wstring rev = word;
reverse(begin(rev), end(rev));
if (rev != word && binary_search(begin(wordvector), end(wordvector), rev))
{
auto candidate1 = make_pair(word, rev);
auto candidate2 = make_pair(rev, word);
if (find(begin(result), end(result), candidate1) == end(result) &&
find(begin(result), end(result), candidate2) == end(result))
result.push_back(candidate1);
}
}
});
return result;
}
此示例产生下面的示例输出。
Downloading 'The Iliad'...
Running serial version... took 953 ms.
Running parallel version... took 656 ms.
The most common words that have five or more letters are:
their (953)
shall (444)
which (431)
great (398)
Hector (349)
Achilles (309)
through (301)
these (268)
chief (259)
The longest sequence of words that have the same first letter is:
through the tempest to the tented
The following palindromes appear in the text:
spots stops
speed deeps
keels sleek
此示例使用 parallel_invoke 算法调用作用于相同数据源的多个函数。 您可以使用 parallel_invoke 算法来并行调用任何函数集,而不仅仅是那些作用于相同数据的函数集。
由于 parallel_invoke 算法将并行调用每个工作函数,因此其性能由花费最长时间完成的函数(即,如果运行时在单独的处理器上处理每个函数)限定。 如果此示例并行执行的任务数多于可用处理器的数目,则会在每个处理器上运行多项任务。 在此情况下,性能由花费最长时间完成的任务组限定。
因为此示例并行执行三项任务,所以您不应期望性能在超过三个处理器的计算机上会得到扩展。 若要进一步提高性能,您可以将运行时间最长的任务分为更小的任务,然后并行运行这些任务。
您可以使用parallel_invoke算法而不是 concurrency::task_group 和 concurrency::structured_task_group 类,如果您不需要支持取消。 有关比较 parallel_invoke 算法与任务组的用法的示例,请参见如何:使用 parallel_invoke 来编写并行排序运行时。
编译代码
若要编译代码,将其复制然后将其粘贴在 Visual Studio 项目中,或将它粘贴到名为的文件并行的 word-mining.cpp ,然后在 Visual Studio 命令提示符窗口中运行以下命令。
cl.exe /EHsc /MD /DUNICODE /D_AFXDLL parallel-word-mining.cpp