खोज…
वाक्य - विन्यास
- धागा ()
- धागा (धागा और अन्य)
- स्पष्ट सूत्र
पैरामीटर
पैरामीटर | विवरण |
---|---|
other | other स्वामित्व लेता है, other अब धागे के मालिक नहीं हैं |
func | एक अलग थ्रेड में कॉल करने का कार्य |
args | के लिए तर्क func |
टिप्पणियों
कुछ नोट:
- दो
std::thread
ऑब्जेक्ट्स कभी भी एक ही थ्रेड का प्रतिनिधित्व नहीं कर सकते हैं। - एक
std::thread
ऑब्जेक्ट उस स्थिति में हो सकता है जहां यह किसी भी थ्रेड का प्रतिनिधित्व नहीं करता है (यानी एक मूव के बाद, कॉल आदिjoin
बाद)।
धागा संचालन
जब आप एक धागा शुरू करते हैं, तो यह समाप्त होने तक निष्पादित होगा।
अक्सर, कुछ बिंदु पर, आपको (संभवतः - धागा पहले से ही किया जा सकता है) धागा समाप्त होने की प्रतीक्षा करें, क्योंकि आप उदाहरण के लिए परिणाम का उपयोग करना चाहते हैं।
int n;
std::thread thread{ calculateSomething, std::ref(n) };
//Doing some other stuff
//We need 'n' now!
//Wait for the thread to finish - if it is not already done
thread.join();
//Now 'n' has the result of the calculation done in the seperate thread
std::cout << n << '\n';
आप थ्रेड को detach
भी कर सकते हैं, इसे स्वतंत्र रूप से निष्पादित करने दें:
std::thread thread{ doSomething };
//Detaching the thread, we don't need it anymore (for whatever reason)
thread.detach();
//The thread will terminate when it is done, or when the main thread returns
एक थ्रेड के संदर्भ में पासिंग
आप किसी संदर्भ (या const
सन्दर्भ) को सीधे थ्रेड में नहीं भेज सकते क्योंकि std::thread
कॉपी / मूव करेगा। इसके बजाय, std::reference_wrapper
उपयोग करें:
void foo(int& b)
{
b = 10;
}
int a = 1;
std::thread thread{ foo, std::ref(a) }; //'a' is now really passed as reference
thread.join();
std::cout << a << '\n'; //Outputs 10
void bar(const ComplexObject& co)
{
co.doCalculations();
}
ComplexObject object;
std::thread thread{ bar, std::cref(object) }; //'object' is passed as const&
thread.join();
std::cout << object.getResult() << '\n'; //Outputs the result
डंठल बनाना :: धागा
C ++ में, थ्रेड्स :: थ्रेड क्लास का उपयोग करके बनाए जाते हैं। एक धागा निष्पादन का एक अलग प्रवाह है; जब आप एक साथ एक और प्रदर्शन करते हैं तो यह एक सहायक एक कार्य करने के लिए अनुरूप होता है। जब थ्रेड में सभी कोड निष्पादित होते हैं, तो यह समाप्त हो जाता है । एक धागा बनाते समय, आपको उस पर निष्पादित होने के लिए कुछ पास करना होगा। कुछ चीजें जो आप एक धागे में बदल सकते हैं:
- नि: शुल्क कार्य
- सदस्य कार्य
- फ़नकार की वस्तुएँ
- लम्बोदर भाव
नि: शुल्क फ़ंक्शन उदाहरण - एक अलग थ्रेड पर एक फ़ंक्शन निष्पादित करता है ( लाइव उदाहरण ):
#include <iostream>
#include <thread>
void foo(int a)
{
std::cout << a << '\n';
}
int main()
{
// Create and execute the thread
std::thread thread(foo, 10); // foo is the function to execute, 10 is the
// argument to pass to it
// Keep going; the thread is executed separately
// Wait for the thread to finish; we stay here until it is done
thread.join();
return 0;
}
सदस्य फ़ंक्शन उदाहरण - एक अलग थ्रेड पर एक सदस्य फ़ंक्शन निष्पादित करता है ( लाइव उदाहरण ):
#include <iostream>
#include <thread>
class Bar
{
public:
void foo(int a)
{
std::cout << a << '\n';
}
};
int main()
{
Bar bar;
// Create and execute the thread
std::thread thread(&Bar::foo, &bar, 10); // Pass 10 to member function
// The member function will be executed in a separate thread
// Wait for the thread to finish, this is a blocking operation
thread.join();
return 0;
}
फ़नकार ऑब्जेक्ट उदाहरण ( लाइव उदाहरण ):
#include <iostream>
#include <thread>
class Bar
{
public:
void operator()(int a)
{
std::cout << a << '\n';
}
};
int main()
{
Bar bar;
// Create and execute the thread
std::thread thread(bar, 10); // Pass 10 to functor object
// The functor object will be executed in a separate thread
// Wait for the thread to finish, this is a blocking operation
thread.join();
return 0;
}
लैम्ब्डा अभिव्यक्ति उदाहरण ( लाइव उदाहरण ):
#include <iostream>
#include <thread>
int main()
{
auto lambda = [](int a) { std::cout << a << '\n'; };
// Create and execute the thread
std::thread thread(lambda, 10); // Pass 10 to the lambda expression
// The lambda expression will be executed in a separate thread
// Wait for the thread to finish, this is a blocking operation
thread.join();
return 0;
}
वर्तमान धागे पर संचालन
std::this_thread
एक ऐसा namespace
जिसमें फ़ंक्शन से वर्तमान थ्रेड पर दिलचस्प चीजें करने के लिए कार्य किया जाता है।
समारोह | विवरण |
---|---|
get_id | धागे की आईडी लौटाता है |
sleep_for | समय की एक निर्दिष्ट राशि के लिए सोता है |
sleep_until | एक विशिष्ट समय तक सोता है |
yield | अन्य थ्रेड को प्राथमिकता देते हुए, थ्रेड्स को फिर से चलाना |
std::this_thread::get_id
का उपयोग करके वर्तमान थ्रेड आईडी प्राप्त करना std::this_thread::get_id
:
void foo()
{
//Print this threads id
std::cout << std::this_thread::get_id() << '\n';
}
std::thread thread{ foo };
thread.join(); //'threads' id has now been printed, should be something like 12556
foo(); //The id of the main thread is printed, should be something like 2420
std::this_thread::sleep_for
का उपयोग करके 3 सेकंड के लिए सो रही है std::this_thread::sleep_for
:
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(3));
}
std::thread thread{ foo };
foo.join();
std::cout << "Waited for 3 seconds!\n";
भविष्य में std::this_thread::sleep_until
का उपयोग करते हुए 3 घंटे तक सोना std::this_thread::sleep_until
:
void foo()
{
std::this_thread::sleep_until(std::chrono::system_clock::now() + std::chrono::hours(3));
}
std::thread thread{ foo };
thread.join();
std::cout << "We are now located 3 hours after the thread has been called\n";
अन्य थ्रेड्स को std::this_thread::yield
का उपयोग करके प्राथमिकता देने दें std::this_thread::yield
:
void foo(int a)
{
for (int i = 0; i < al ++i)
std::this_thread::yield(); //Now other threads take priority, because this thread
//isn't doing anything important
std::cout << "Hello World!\n";
}
std::thread thread{ foo, 10 };
thread.join();
एसटीडी के बजाय एसटीडी :: थ्रेड का उपयोग करना
std::async
धागे बनाने में भी सक्षम है। std::thread
तुलना में इसे कम शक्तिशाली माना जाता है लेकिन इसका उपयोग करना तब आसान होता है जब आप किसी फ़ंक्शन को अतुल्यकालिक रूप से चलाना चाहते हैं।
अतुल्यकालिक रूप से एक फ़ंक्शन को कॉल करना
#include <future>
#include <iostream>
unsigned int square(unsigned int i){
return i*i;
}
int main() {
auto f = std::async(std::launch::async, square, 8);
std::cout << "square currently running\n"; //do something while square is running
std::cout << "result is " << f.get() << '\n'; //getting the result from square
}
आम नुकसान
std::async
रिटर्न एकstd::future
जो रिटर्न मान रखता है जो फ़ंक्शन द्वारा गणना की जाएगी। जब वहfuture
नष्ट हो जाता है तो वह तब तक इंतजार करता है जब तक कि धागा पूरा नहीं हो जाता है, जिससे आपका कोड प्रभावी रूप से सिंगल थ्रेडेड हो जाता है। जब आपको रिटर्न वैल्यू की आवश्यकता नहीं होती है तो इसे आसानी से अनदेखा कर दिया जाता है:std::async(std::launch::async, square, 5); //thread already completed at this point, because the returning future got destroyed
std::async
एक प्रक्षेपण नीति के बिना काम करता है, इसलिएstd::async(square, 5);
संकलित करता है। जब आप ऐसा करते हैं तो सिस्टम को यह तय करना होता है कि वह थ्रेड बनाना चाहता है या नहीं। यह विचार था कि सिस्टम एक धागा बनाने का विकल्प चुनता है जब तक कि यह पहले से अधिक धागे से नहीं चल रहा है जब तक कि यह कुशलता से नहीं चल सकता है। दुर्भाग्य से आमतौर पर कार्यान्वयन केवल उस स्थिति में एक धागा बनाने के लिए नहीं चुनते हैं, कभी भी, इसलिए आपकोstd::launch::async
साथ उस व्यवहार को ओवरराइड करने की आवश्यकता है जो सिस्टम को थ्रेड बनाने के लिए मजबूर करता है।दौड़ की स्थिति से सावधान रहें।
वायदा और वादे पर async पर अधिक
एक धागा सुनिश्चित करना हमेशा शामिल हो जाता है
जब std::thread
लिए std::thread
को लागू किया जाता है, तो या तो join()
लिए एक कॉल join()
या detach()
होगी । यदि कोई धागा शामिल नहीं हुआ है या अलग नहीं किया गया है, तो डिफ़ॉल्ट रूप से std::terminate
को बुलाया जाएगा। RAII का उपयोग करना, यह आमतौर पर पूरा करने के लिए काफी सरल है:
class thread_joiner
{
public:
thread_joiner(std::thread t)
: t_(std::move(t))
{ }
~thread_joiner()
{
if(t_.joinable()) {
t_.join();
}
}
private:
std::thread t_;
}
यह तो इस तरह प्रयोग किया जाता है:
void perform_work()
{
// Perform some work
}
void t()
{
thread_joiner j{std::thread(perform_work)};
// Do some other calculations while thread is running
} // Thread is automatically joined here
यह अपवाद सुरक्षा भी प्रदान करता है; यदि हमने अपना धागा सामान्य रूप से बनाया था और अन्य गणनाओं को करने के लिए t()
में किए गए कार्य ने एक अपवाद को फेंक दिया था, तो हमारे धागे पर join()
को कभी नहीं बुलाया गया था और हमारी प्रक्रिया समाप्त हो गई होगी।
धागा वस्तुओं को फिर से सौंपना
हम खाली थ्रेड ऑब्जेक्ट्स बना सकते हैं और बाद में उन्हें काम सौंप सकते हैं।
यदि हम एक थ्रेड ऑब्जेक्ट को किसी अन्य सक्रिय, joinable
करने योग्य थ्रेड पर असाइन करते हैं, तो std::terminate
होने से पहले थ्रेड को बदलने के लिए स्वचालित रूप से कॉल किया जाएगा।
#include <thread>
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(3));
}
//create 100 thread objects that do nothing
std::thread executors[100];
// Some code
// I want to create some threads now
for (int i = 0;i < 100;i++)
{
// If this object doesn't have a thread assigned
if (!executors[i].joinable())
executors[i] = std::thread(foo);
}
मूल तुल्यकालन
थ्रेड सिंक्रोनाइजेशन म्यूटेक्स का उपयोग करके पूरा किया जा सकता है, अन्य सिंक्रनाइज़ेशन प्राइमरी के बीच। मानक पुस्तकालय द्वारा कई म्यूटेक्स प्रकार प्रदान किए जाते हैं, लेकिन सबसे सरल std::mutex
। म्यूटेक्स को लॉक करने के लिए, आप उस पर एक लॉक का निर्माण करते हैं। सबसे सरल लॉक प्रकार std::lock_guard
:
std::mutex m;
void worker() {
std::lock_guard<std::mutex> guard(m); // Acquires a lock on the mutex
// Synchronized code here
} // the mutex is automatically released when guard goes out of scope
std::lock_guard
के साथ mutex लॉक ऑब्जेक्ट के पूरे जीवनकाल के लिए लॉक होता है। उन मामलों में जहां आपको लॉकिंग के लिए क्षेत्रों को मैन्युअल रूप से नियंत्रित करने की आवश्यकता है, इसके बजाय std::unique_lock
उपयोग करें:
std::mutex m;
void worker() {
// by default, constructing a unique_lock from a mutex will lock the mutex
// by passing the std::defer_lock as a second argument, we
// can construct the guard in an unlocked state instead and
// manually lock later.
std::unique_lock<std::mutex> guard(m, std::defer_lock);
// the mutex is not locked yet!
guard.lock();
// critical section
guard.unlock();
// mutex is again released
}
अधिक थ्रेड सिंक्रनाइज़ेशन संरचनाएँ
हालत चर का उपयोग करना
एक स्थिति चर एक म्यूटेक्स है जिसका उपयोग थ्रेड्स के बीच संचार को ऑर्केस्ट्रेट करने के लिए म्यूटेक्स के साथ किया जाता है। हालांकि, यह पूरा करने के लिए न तो अनन्य या सबसे कुशल तरीका है, यह पैटर्न से परिचित लोगों के लिए सबसे सरल हो सकता है।
एक std::condition_variable
पर प्रतीक्षा करता है std::condition_variable
with a std::unique_lock<std::mutex>
। यह कोड को अधिग्रहण के साथ आगे बढ़ने या नहीं तय करने से पहले साझा स्थिति की सुरक्षित रूप से जांच करने की अनुमति देता है।
नीचे एक निर्माता-उपभोक्ता स्केच है जो चीजों को रोचक बनाने के लिए std::thread
, std::condition_variable
, std::mutex
और कुछ अन्य का उपयोग करता है।
#include <condition_variable>
#include <cstddef>
#include <iostream>
#include <mutex>
#include <queue>
#include <random>
#include <thread>
int main()
{
std::condition_variable cond;
std::mutex mtx;
std::queue<int> intq;
bool stopped = false;
std::thread producer{[&]()
{
// Prepare a random number generator.
// Our producer will simply push random numbers to intq.
//
std::default_random_engine gen{};
std::uniform_int_distribution<int> dist{};
std::size_t count = 4006;
while(count--)
{
// Always lock before changing
// state guarded by a mutex and
// condition_variable (a.k.a. "condvar").
std::lock_guard<std::mutex> L{mtx};
// Push a random int into the queue
intq.push(dist(gen));
// Tell the consumer it has an int
cond.notify_one();
}
// All done.
// Acquire the lock, set the stopped flag,
// then inform the consumer.
std::lock_guard<std::mutex> L{mtx};
std::cout << "Producer is done!" << std::endl;
stopped = true;
cond.notify_one();
}};
std::thread consumer{[&]()
{
do{
std::unique_lock<std::mutex> L{mtx};
cond.wait(L,[&]()
{
// Acquire the lock only if
// we've stopped or the queue
// isn't empty
return stopped || ! intq.empty();
});
// We own the mutex here; pop the queue
// until it empties out.
while( ! intq.empty())
{
const auto val = intq.front();
intq.pop();
std::cout << "Consumer popped: " << val << std::endl;
}
if(stopped){
// producer has signaled a stop
std::cout << "Consumer is done!" << std::endl;
break;
}
}while(true);
}};
consumer.join();
producer.join();
std::cout << "Example Completed!" << std::endl;
return 0;
}
एक साधारण धागा पूल बनाएँ
C ++ 11 सूत्रण प्रधानताएं अभी भी अपेक्षाकृत निम्न स्तर की हैं। उनका उपयोग थ्रेड पूल की तरह उच्च स्तर के निर्माण को लिखने के लिए किया जा सकता है:
struct tasks {
// the mutex, condition variable and deque form a single
// thread-safe triggered queue of tasks:
std::mutex m;
std::condition_variable v;
// note that a packaged_task<void> can store a packaged_task<R>:
std::deque<std::packaged_task<void()>> work;
// this holds futures representing the worker threads being done:
std::vector<std::future<void>> finished;
// queue( lambda ) will enqueue the lambda into the tasks for the threads
// to use. A future of the type the lambda returns is given to let you get
// the result out.
template<class F, class R=std::result_of_t<F&()>>
std::future<R> queue(F&& f) {
// wrap the function object into a packaged task, splitting
// execution from the return value:
std::packaged_task<R()> p(std::forward<F>(f));
auto r=p.get_future(); // get the return value before we hand off the task
{
std::unique_lock<std::mutex> l(m);
work.emplace_back(std::move(p)); // store the task<R()> as a task<void()>
}
v.notify_one(); // wake a thread to work on the task
return r; // return the future result of the task
}
// start N threads in the thread pool.
void start(std::size_t N=1){
for (std::size_t i = 0; i < N; ++i)
{
// each thread is a std::async running this->thread_task():
finished.push_back(
std::async(
std::launch::async,
[this]{ thread_task(); }
)
);
}
}
// abort() cancels all non-started tasks, and tells every working thread
// stop running, and waits for them to finish up.
void abort() {
cancel_pending();
finish();
}
// cancel_pending() merely cancels all non-started tasks:
void cancel_pending() {
std::unique_lock<std::mutex> l(m);
work.clear();
}
// finish enques a "stop the thread" message for every thread, then waits for them:
void finish() {
{
std::unique_lock<std::mutex> l(m);
for(auto&&unused:finished){
work.push_back({});
}
}
v.notify_all();
finished.clear();
}
~tasks() {
finish();
}
private:
// the work that a worker thread does:
void thread_task() {
while(true){
// pop a task off the queue:
std::packaged_task<void()> f;
{
// usual thread-safe queue code:
std::unique_lock<std::mutex> l(m);
if (work.empty()){
v.wait(l,[&]{return !work.empty();});
}
f = std::move(work.front());
work.pop_front();
}
// if the task is invalid, it means we are asked to abort:
if (!f.valid()) return;
// otherwise, run the task:
f();
}
}
};
tasks.queue( []{ return "hello world"s; } )
एक std::future<std::string>
देता है std::future<std::string>
, जो जब कार्य ऑब्जेक्ट को चलाने के लिए इधर-उधर होता है, तो यह hello world
साथ पॉप्युलेट हो जाता hello world
।
आप कार्य चलाकर थ्रेड बनाते हैं। tasks.start(10)
(जो 10 थ्रेड शुरू होता है)।
packaged_task<void()>
का उपयोग packaged_task<void()>
केवल इसलिए है क्योंकि कोई प्रकार-मिटाया गया std::function
समतुल्य नहीं है जो केवल-प्रकार के प्रकारों को संग्रहीत करता है। उन में से एक कस्टम लिखना संभवत: packaged_task<void()>
का उपयोग करने से अधिक तेज़ होगा।
C ++ 11 में, result_of_t<blah>
को typename result_of<blah>::type
।
म्यूटेक्स पर अधिक।
धागा-स्थानीय भंडारण
थ्रेड-लोकल स्टोरेज को thread_local
कीवर्ड का उपयोग करके बनाया जा सकता है। thread_local
साथ घोषित एक वेरिएबल को थ्रेड स्टोरेज अवधि कहा जाता है ।
- किसी प्रोग्राम में प्रत्येक थ्रेड की प्रत्येक थ्रेड-स्थानीय चर की अपनी प्रतिलिपि है।
- फ़ंक्शन (स्थानीय) स्कोप के साथ एक थ्रेड-लोकल वैरिएबल को इनिशियलाइज़ किया जाएगा जो कि पहली बार कंट्रोल अपनी परिभाषा से गुजरता है। जब तक
extern
घोषित नहीं किया जाता है तब तक ऐसा वैरिएबल स्पष्ट रूप से स्थिर होता है। - थ्रेड स्टार्टअप के भाग के रूप में नाम स्थान या वर्ग (गैर-स्थानीय) स्कोप के साथ एक थ्रेड-लोकल वैरिएबल को इनिशियलाइज़ किया जाएगा।
- थ्रेड-स्थानीय चर थ्रेड समाप्ति पर नष्ट हो जाते हैं।
- किसी वर्ग का सदस्य केवल थ्रेड-लोकल हो सकता है अगर वह स्थिर हो। इसलिए प्रति चर (थ्रेड, उदाहरण) जोड़ी की बजाय एक प्रति प्रति उस चर की एक प्रति होगी।
उदाहरण:
void debug_counter() {
thread_local int count = 0;
Logger::log("This function has been called %d times by this thread", ++count);
}