我有一些代码,我必须写,以替换一个字面上使用了数千次的函数。该函数的问题是返回一个指向静态分配缓冲区的指针,并且存在可疑问题。我终于能够证明间歇性的高负荷误差是由不良做法引起的。
我替换的函数的签名为char * paddandtruncate(char *,int)
,char * paddandtruncate(float,int)
或char * paddandtruncat(int,int)
。每个函数都返回一个指向静态分配缓冲区的指针,该缓冲区在后续调用时被覆盖。
我有三个常数
我想要一些关于风格和可能的重构想法的意见。
系统基于用空格填充的固定宽度字段,并且存在一些架构问题。由于项目规模约为1,000,000行,因此无法解决这些问题。
我最初计划在创建后允许更改数据,但认为不可变对象提供了更安全的解决方案。
using namespace std;
class SYSTEM_DECLSPEC CoreString
{
private:
friend ostream & operator<<(ostream &os,CoreString &cs);
stringstream m_SS ;
float m_FltData ;
long m_lngData ;
long m_Width ;
string m_strData ;
string m_FormatedData;
bool m_Formated ;
stringstream SS ;
public:
CoreString(const string &InStr,long Width):
m_Formated(false),
m_Width(Width),
m_strData(InStr)
{
long OldFlags = SS.flags();
SS.fill(' ');
SS.width(Width);
SS.flags(ios::left);
SS<<InStr;
m_FormatedData = SS.str();
}
CoreString(long longData , long Width):
m_Formated(false),
m_Width(Width),
m_lngData(longData)
{
long OldFlags = SS.flags();
SS.fill('0');
SS.precision(0);
SS.width(Width);
SS.flags(ios::right);
SS<<longData;
m_FormatedData = SS.str();
}
CoreString(float FltData, long width,long lPerprecision):
m_Formated(false),
m_Width(width),
m_FltData(FltData)
{
long OldFlags = SS.flags();
SS.fill('0');
SS.precision(lPerprecision);
SS.width(width);
SS.flags(ios::right);
SS<<FltData;
m_FormatedData = SS.str();
}
CoreString(const string &InStr):
m_Formated(false),
m_strData(InStr)
{
long OldFlags = SS.flags();
SS.fill(' ');
SS.width(32);
SS.flags(ios::left);
SS<<InStr;
m_FormatedData = SS.str();
}
public:
operator const char *() {return m_FormatedData.c_str();}
operator const string& () const {return m_FormatedData;}
const string& str() const ;
};
const string& CoreString::str() const
{
return m_FormatedData;
}
ostream & operator<<(ostream &os,CoreString &cs)
{
os<< cs.m_Formated;
return os;
}
答案 0 :(得分:2)
如果你的意思是“对呼叫者没有影响”,那么你的选择非常有限。您无法返回需要被调用者释放的任何内容。
冒着用另一个解决方案替换一个错误解决方案的风险,最快和最简单的解决方案可能就是:不使用单个静态缓冲区,而是使用它们的池并在每次调用函数时旋转它们。确保选择缓冲区的代码是线程安全的。
答案 1 :(得分:2)
听起来系统是有线的,对吗?如果只是在你仍在使用前一个输出时再次调用其中一个函数是不安全的,那么它每次都应该表现相同。
大多数编译器都有办法将变量标记为“线程局部数据”,以便它具有不同的地址,具体取决于访问它的线程。在gcc中它是__thread
,在VC ++中它是__declspec(thread)
。
如果您需要能够从同一个线程多次调用这些函数而不覆盖结果,我看不到任何完整的解决方案,但强制调用者释放结果。您可以使用混合方法,其中每个线程都有固定数量的缓冲区,这样无论其他线程在做什么,调用者都可以在不覆盖以前结果的情况下进行N次调用。
答案 2 :(得分:1)
您发布的代码有一个巨大的问题 - 如果调用者将返回值分配给const char *,编译器将进行静默转换并销毁您的临时CoreString对象。现在你的指针无效。
答案 3 :(得分:1)
我不知道调用者将如何使用它,但使用new
将缓冲区分配到auto_ptr<>
可能有效。它可能满足标准1(我看不到使用代码时无法分辨),并且可能是一个非常快速的修复。最大的问题是它使用了很多动态内存,这会减慢速度。你可以做的事情,使用新的等等,但这可能不会很快编码。
如果你不能使用动态存储,那么你只能使用非动态存储,如果不使用缓冲区或线程局部缓冲区或类似的东西,你真的没有多少工作。< / p>
答案 4 :(得分:0)
“间歇性高负载错误”是由竞争条件引起的,其中一个线程在另一个线程完成使用之前在静态缓冲区上进行了践踏,对吗?
所以切换到每个线程使用一个输出缓冲区,使用你的平台提供的任何线程本地存储机制(Windows,我在想)。
没有同步争用,线程之间没有干扰,并且基于你所说的关于当前实现旋转缓冲区的内容,几乎可以肯定调用代码根本不需要改变。如果当前实现使用多个缓冲区,则不能依赖每次使用的相同缓冲区。
我可能不会从头开始以这种方式设计API,但它会实现您当前的API,而不会以显着的方式更改它,或影响性能。