![](/img/trans.png)
[英]C++ Is writing and reading a variable from different threads undefined behavior
[英]Writing file from different threads in C++
我正在編寫一個從不同線程寫入二進制文件的程序。 每個線程將寫入文件的不同位置。 我不使用同步,程序也可以正常工作。 我想問一下您是否應該使用一些同步,以及如何或是否足以讓OS同步以某種方式執行此操作。 我使用的是Linux,gcc編譯器,但有時也可以在其他平台上運行。 我使用以下函數從不同的線程寫入文件。
void writeBytesFrom(std::string fileName, uint64_t fromPosition, uint8_t* buffer, int numBytes)
{
if(CHAR_BIT != 8)
{
std::stringstream errMsg;
errMsg << "Can not use this platform since CHAR_BIT size is not 8, namely it is "
<< CHAR_BIT << ".";
LOGE << errMsg.str();
throw std::runtime_error(errMsg.str());
}
std::ofstream file(fileName,
std::ios::binary | std::ios::out
| std::ios::in); // Open binary, for output, for input
if(!file.is_open()) // cannot open file
{
std::stringstream errMsg;
errMsg << "Can not open file " << fileName << ".";
LOGE << errMsg.str();
throw std::runtime_error(errMsg.str());
}
file.seekp(fromPosition); // put pointer
std::streampos cur = file.tellp();
file.write((char*)buffer, numBytes);
auto pos = file.tellp();
auto num = pos - cur;
file.close();
if(num != numBytes)
{
std::stringstream errMsg;
errMsg << num << " bytes written from number of bytess that should be written "
<< numBytes << " to " << fileName << ".";
LOGE << errMsg.str();
throw std::runtime_error(errMsg.str());
}
}
如果您有任何其他改進我的代碼的建議,我願意接受。 我使用uint8_t緩沖區的原因是,對我來說,理解緩沖區代表8位字節比使用無符號字符更自然。 我知道有些純粹主義者可能對此表示反對。
謝謝,伏伊塔。
如果您有任何其他改進我的代碼的建議,我願意接受。
並非每個人都會同意每個注釋或代碼樣式建議,但是這些對我有用:
#include <string>
#include <climits>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <vector>
extern std::ostream& LOGE;
// See below for the reasons I write this
struct unwrapper {
void print_exception(std::ostream&os, const std::exception& e, int level = 0) const
{
os << level << " : " << e.what() << '\n';
try {
std::rethrow_if_nested(e);
} catch(const std::exception& e) {
print_exception(os, e, level+1);
} catch(...) {
os << level << " : nonstandard exception";
}
}
std::exception const& initial_exception;
friend std::ostream& operator<<(std::ostream& os, unwrapper const& u)
{
u.print_exception(os, u.initial_exception);
return os;
}
};
unwrapper unwrap(std::exception const& e)
{
return unwrapper { e };
}
// Comment #3 : separate concerns - opening the outfile is a separate job for which
// we will want a discrete error. So wrap that logic into a function
// Comment #4 : separate exception handling from code
// Comment #5 : nested exceptions are great for providing a forensic trail
// which helps us to solve runtime errors
// Comment #2 : an ofstream has no business being open for input
// std::ios::out is assumed
// Comment #6 : since we're using exceptions, let the stream object raise them for us
std::ofstream openOutput(std::string const& fileName)
try
{
std::ofstream file(fileName, std::ios::binary);
file.exceptions(std::ios::badbit | std::ios::failbit);
return file;
}
catch(std::exception&)
{
auto message = [&]() {
std::ostringstream ss;
ss << "openOutput: fileName = " << std::quoted(fileName);
return std::move(ss).str();
} ();
LOGE << message;
std::throw_with_nested(std::runtime_error(message));
}
void writeBytesFrom(std::string fileName, uint64_t fromPosition, uint8_t* buffer, int numBytes)
try // function try blocks separate exception handling from logic
{
// Comment #1 : prefer non-compilation over runtime failures.
// Comment #7 if too few bytes are written, this will now throw
// you don't need to check
// Comment #8 file.close() is automatic, but harmless to leave in
static_assert(CHAR_BIT == 8, "Can not use this platform since CHAR_BIT size is not 8");
std::ofstream file(fileName, std::ios::binary); // Open binary, for output, for input
file.seekp(fromPosition); // put pointer
file.write((char*)buffer, numBytes);
file.close();
}
catch(std::exception&)
{
auto message = [&]() {
std::ostringstream ss;
ss << "writeBytesFrom: fileName = " << std::quoted(fileName) << ", fromPosition = " << fromPosition;
return std::move(ss).str();
} ();
LOGE << message;
std::throw_with_nested(std::runtime_error(message));
}
void test()
{
extern std::string getfile();
extern std::vector<std::uint8_t>& getbuffer();
extern uint64_t getpos();
auto&& file = getfile();
auto&& buffer = getbuffer();
auto pos = getpos();
try
{
writeBytesFrom(file, pos, buffer.data(), buffer.size());
}
catch(std::exception& e)
{
// Comment #9 we can unwrap nested exceptions into one log line to
// provide a complete history of the error
LOGE << "test failed to write: " << unwrap(e);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.