잔글 (static이 필요 없다.) |
|||
1번째 줄: | 1번째 줄: | ||
[[분류:C++]] | [[분류:C++]] | ||
+ | [[분류:프로그래밍]] | ||
원래 쓰레드 마다 별개의 정보를 기록할 필요가 있는 경우는 그다지 많지 않다만, 필요한 경우가 있다. 예를 들면 공통된 데이터를 가지고 스레드들이 공유하며 별개의 데이터를 생산해야 할 때 필요하다. | 원래 쓰레드 마다 별개의 정보를 기록할 필요가 있는 경우는 그다지 많지 않다만, 필요한 경우가 있다. 예를 들면 공통된 데이터를 가지고 스레드들이 공유하며 별개의 데이터를 생산해야 할 때 필요하다. | ||
다음 코드는 C++에서 스레드마다 별개의 데이터를 가져야 하는 경우를 보여준다. | 다음 코드는 C++에서 스레드마다 별개의 데이터를 가져야 하는 경우를 보여준다. |
2018년 11월 6일 (화) 12:38 판
원래 쓰레드 마다 별개의 정보를 기록할 필요가 있는 경우는 그다지 많지 않다만, 필요한 경우가 있다. 예를 들면 공통된 데이터를 가지고 스레드들이 공유하며 별개의 데이터를 생산해야 할 때 필요하다. 다음 코드는 C++에서 스레드마다 별개의 데이터를 가져야 하는 경우를 보여준다.
#include <iostream>
#include <memory>
#include <mutex>
#include <optional>
class Tokeniser
{
public:
Tokeniser(const char * str, char pattern):m_str(str), m_pattern(pattern)
{
}
std::optional<size_t> Next()
{
thread_local size_t last_index = -1;//static이 필요없다...
while (m_str[++last_index] != '\0')
{
if (m_str[last_index] == m_pattern)
{
return std::optional<size_t>(last_index);
}
}
return std::nullopt;
}
const char* GetStr() const
{
return m_str;
}
private:
char m_pattern;
const char * m_str;
};
int main()
{
Tokeniser tokeniser("1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37", ' ');
std::thread threads[5];
std::mutex m;
for (std::thread& thread : threads)
{
thread = std::move(std::thread([&m, &tokeniser]() {
size_t pre = 0;
for (auto endindex = tokeniser.Next(); endindex.has_value(); endindex = tokeniser.Next())
{
m.lock();
for (size_t i = pre; i < endindex.value(); i++)
{
std::cout << tokeniser.GetStr()[i];
}
m.unlock();
std::cout << std::endl;
pre = endindex.value();
}
}));
}
for (std::thread& thread : threads)
{
thread.join();
}
return 0;
}
이렇게 스레드마다 별개의 데이터를 가져야 할 때, 외부에서 동적 메모리를 할당하지 않고, 유지할 수 있는 방법을 thread_local 키워드가 제공한다.
windows에서는 C++11이 나오기 전에 TLS라 하여 동일한 개념을 지원했다.
사실 이런 경우는 iterator패턴을 쓰면 된다.
#include <iostream>
#include <memory>
#include <mutex>
#include <optional>
class Tokeniser
{
public:
Tokeniser(const char * str, char pattern):m_str(str), m_pattern(pattern)
{
}
class Token
{
public:
Token(Tokeniser* tokenizer,const std::optional<size_t>& i) :m_tokenizer(tokenizer), lastIndex(i)
{
if (lastIndex.has_value() == false) return;
size_t last_index = lastIndex.value();
while (m_tokenizer->m_str[++last_index] != '\0')
{
if (m_tokenizer->m_str[last_index] == m_tokenizer->m_pattern)
{
lastIndex = std::optional<size_t>(last_index);
return;
}
}
lastIndex = std::nullopt;
}
void operator ++()
{
if (lastIndex.has_value() == false) return;
size_t last_index = lastIndex.value();
while (m_tokenizer->m_str[++last_index] != '\0')
{
if (m_tokenizer->m_str[last_index] == m_tokenizer->m_pattern)
{
lastIndex = std::optional<size_t>(last_index);
return;
}
}
lastIndex= std::nullopt;
}
bool operator != (const Token & ref)
{
return ref.lastIndex.has_value() == false && this->lastIndex.has_value() == true;
}
size_t operator * ()
{
return lastIndex.value();
}
std::optional<size_t> lastIndex;
Tokeniser * m_tokenizer;
};
Token begin()
{
return Token(this, std::optional<size_t>(-1));
}
Token end()
{
return Token(this, std::nullopt);
}
const char* GetStr() const
{
return m_str;
}
private:
char m_pattern;
const char * m_str;
};
int main()
{
Tokeniser tokeniser("1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37", ' ');
std::thread threads[5];
std::mutex m;
for (std::thread& thread : threads)
{
thread = std::move(std::thread([&m, &tokeniser]() {
size_t pre = 0;
for (size_t it :tokeniser)
{
m.lock();
for (size_t i = pre; i < it; i++)
{
std::cout << tokeniser.GetStr()[i];
}
m.unlock();
std::cout << std::endl;
pre = it;
}
}));
}
for (std::thread& thread : threads)
{
thread.join();
}
return 0;
}