Reliably checking if a thread can be created [duplicate]

EDIT: Having figured it out (see my own answer), I get that the question text below is misleading, which caused a lot of misunderstanding. I still believe it might help somebody who has the same issue.

Justification

I am asking this question (at least in its essence) now the third time, since the first and second attempt drifted off into discussions about details I did not intend.

I am nonetheless convinced that an answer to the actual core question would be a valuable contribution to people (like myself) who are not experts in multi-thread programming, since it is currently not easily found on the internet and poses a valid problem.

Question

Say we have a function fct to be run in parallel using threads. Now, it is OS-dependent if a thread can be spawned at a given moment, depending on the available RAM and other things. If it’s not possible, and we still naively try to do so, the program will crash. So, how can we reliably decide if a thread can be spawned, and otherwise decide against it, such that the program doesn’t crash uncontrollably?

(As others pointed out, the possible number of concurrently running threads is “usually” “very high”. I have two different Windows machines, though, one with 48GB of RAM (Windows 10; VS 2019), the other with 64GB of RAM (Windows 11; VS 2022) for both of which this number is somewhere around <20 for the below (extremely simple!) example. For the actual, more complex code I try to improve, I am stuck with only 5 threads. The point is not to improve this number or find out why my machines are shitty. I rather want my code to run crash-free on any OS it encounters.)

To have a ground truth, consider this MWE:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>#include <iostream>
#include <thread>
#include <vector>
#include <string>
constexpr int NUM_JOBS{ 100 }; // Number of jobs to run.
constexpr int NUM_THREADS{ 15 }; // Number of threads run at most in parallel. Setting this to 20 crashes on my machine.
void fct(const std::string& str) // Some function to run in parallel.
{
std::cout << "Processing job: " + str + "n";
}
int main(int argc, char* argv[])
{
std::vector<std::string> job_descriptions{}; // Different "configs" to call fct.
std::vector<std::thread> threads{};
int numThreads{ 0 };
for (int i = 0; i < NUM_JOBS; i++) { // Create some "configs".
job_descriptions.push_back("J" + std::to_string(i));
}
for (const auto& job : job_descriptions) { // Run the jobs, in parallel if possible.
bool spawned{ false };
while (!spawned) {
if (numThreads < NUM_THREADS) { // Spawn new threads only if not more than NUM_THREADS are running simultaneously.
threads.emplace_back(std::thread(fct, job));
spawned = true;
numThreads++;
}
else if (!threads.empty()) { // Otherwise wait for the topmost thread to finish before spawning new ones.
threads.front().join();
threads.erase(threads.begin());
numThreads--;
}
}
}
for (auto& t : threads) t.join(); // Join the remaining threads in the end.
return 0;
}
</code>
<code>#include <iostream> #include <thread> #include <vector> #include <string> constexpr int NUM_JOBS{ 100 }; // Number of jobs to run. constexpr int NUM_THREADS{ 15 }; // Number of threads run at most in parallel. Setting this to 20 crashes on my machine. void fct(const std::string& str) // Some function to run in parallel. { std::cout << "Processing job: " + str + "n"; } int main(int argc, char* argv[]) { std::vector<std::string> job_descriptions{}; // Different "configs" to call fct. std::vector<std::thread> threads{}; int numThreads{ 0 }; for (int i = 0; i < NUM_JOBS; i++) { // Create some "configs". job_descriptions.push_back("J" + std::to_string(i)); } for (const auto& job : job_descriptions) { // Run the jobs, in parallel if possible. bool spawned{ false }; while (!spawned) { if (numThreads < NUM_THREADS) { // Spawn new threads only if not more than NUM_THREADS are running simultaneously. threads.emplace_back(std::thread(fct, job)); spawned = true; numThreads++; } else if (!threads.empty()) { // Otherwise wait for the topmost thread to finish before spawning new ones. threads.front().join(); threads.erase(threads.begin()); numThreads--; } } } for (auto& t : threads) t.join(); // Join the remaining threads in the end. return 0; } </code>
#include <iostream>
#include <thread>
#include <vector>
#include <string>

constexpr int NUM_JOBS{ 100 };   // Number of jobs to run.
constexpr int NUM_THREADS{ 15 }; // Number of threads run at most in parallel. Setting this to 20 crashes on my machine.

void fct(const std::string& str) // Some function to run in parallel.
{
   std::cout << "Processing job: " + str + "n";
}

int main(int argc, char* argv[])
{
   std::vector<std::string> job_descriptions{}; // Different "configs" to call fct.
   std::vector<std::thread> threads{};
   int numThreads{ 0 };

   for (int i = 0; i < NUM_JOBS; i++) { // Create some "configs".
      job_descriptions.push_back("J" + std::to_string(i));
   }
 
   for (const auto& job : job_descriptions) { // Run the jobs, in parallel if possible.
      bool spawned{ false };

      while (!spawned) {
         if (numThreads < NUM_THREADS) { // Spawn new threads only if not more than NUM_THREADS are running simultaneously.
            threads.emplace_back(std::thread(fct, job));
            spawned = true;
            numThreads++;
         }
         else if (!threads.empty()) { // Otherwise wait for the topmost thread to finish before spawning new ones.
            threads.front().join();
            threads.erase(threads.begin());
            numThreads--;
         }
      }
   }

   for (auto& t : threads) t.join(); // Join the remaining threads in the end.

   return 0;
}

The function fct to run in parallel is very simple, it only prints one line to cout. Using some constant NUM_THREADS, we can control how many jobs are run in parallel at most. Setting this number too high yields on my Windows machines:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>Exception thrown at 0x768BA892 in ***.exe: Microsoft C++ exception: std::system_error at memory location 0x06DCFA68.
</code>
<code>Exception thrown at 0x768BA892 in ***.exe: Microsoft C++ exception: std::system_error at memory location 0x06DCFA68. </code>
Exception thrown at 0x768BA892 in ***.exe: Microsoft C++ exception: std::system_error at memory location 0x06DCFA68.

(On Linux, I can set it very high, but that’s, as said, not the point.)

The question is basically, can we replace the condition numThreads < NUM_THREADS with something to the effect of: “If thread can be spawned”?

25

it is OS-dependent if a thread can be spawned at a given moment, depending on the available RAM and other things. If it’s not possible, and we still naively try to do so, the program will crash.

This might technically be true, but in practice, it is not something that anyone worries about, especially on modern desktop computers running Windows (as opposed to tiny embedded systems.) Millions of programmers all over the planet are writing code every day that spawns threads with reckless abandon and without the slightest worry or concern. If you are the only one encountering issues, then clearly, there must be something wrong with what you are doing.

As others pointed out, the possible number of concurrently running threads is “usually” “very high”. I have two different Windows machines, though, […] for both of which this number is somewhere around <20 for the below (extremely simple!) example.

I do not even need to look at your example below, I can tell you with confidence that the number is nowhere around <20, it is thousands upon thousands, and the problem lies in all certainty in your example below. That is immeasurably more probable than both of your machines being “shitty”.

As for the fact that it runs on Linux but not on Windows, this can be fully explained by undefined behavior. If your code triggers undefined behavior, then it may work on one system and not on another, or it might even, by sheer coincidence, work on both systems; it is still wrong.


Now, there is a way to look at the question which is valid: Suppose you are working on a puny, claustrophobic embedded system that does really have a very low limit on the number of threads that it can spawn; how do you know if you can spawn a thread?

The answer here is that if the firmware of the system is written correctly, and if the C++ runtime for that system is written correctly, and if your code is written correctly, then you are not going to get some random crash; instead, it is the constructor of std::thread that will throw an exception.

According to the documentation:

Exceptions
3) std::system_error if the thread could not be started. The exception may represent the error condition std::errc::resource_unavailable_try_again or another implementation-specific error condition.

So, be sure to catch that exception.

4

Suppose you have a function, bool threadCanBeCreated(). And suppose you call it:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>if (threadCanBeCreated()) {
createAThread();
}
else {
doSomethingElse();
}
</code>
<code>if (threadCanBeCreated()) { createAThread(); } else { doSomethingElse(); } </code>
if (threadCanBeCreated()) {
    createAThread();
}
else {
    doSomethingElse();
}

Will it be possible for createAThread() to create a thread? Would it be possible for doSomethingElse() to create a thread?

If the value returned by threadCanBeCreated() could be changed by the action of any other thread in the same program or, by any other process running on the same host, then the answer to both questions is, there is no way of knowing.

After threadCanBeCreated() returns, but before the caller does something about the return value, the answer could change.

1

I FINALLY figured it out, and I’m making this an answer since it could help somebody coming across, as desperate as I have been.

3CxEZiVlQ’s comment was on the spot:

It looks very strange. Having 48GB and 64GB PCs you run 32-bit applications.

The problem seems to be that I compiled in 32-bit. Changing it to 64-bit I’m able to spawn “as many threads as I like”.

(Now, technically this is not an answer to my original question. But I guess for most practical cases this solves all the problems. I still don’t know how to catch the exception in the 32-bit case… But being able to spawn a high number of threads made this question far less important. And at least I understand now, why this was only happening to me and to nobody else on earth…)

3

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật