원래 쓰레드 마다 별개의 정보를 기록할 필요가 있는 경우는 그다지 많지 않다만, 필요한 경우가 있다. 예를 들면 공통된 데이터를 가지고 스레드들이 공유하며 별개의 데이터를 생산해야 할 때 필요하다. 다음 코드는 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;
}