https://www.oschina.net/p/hp-socket
跟一遍代码
打开Windows的Demo TestEcho-UDP
根据《Windows核心编程》错误信息可以自己注册 可以在vs中添加监视@err,hr 获得实时错误信息
void WINAPI SetLastError(
_In_ DWORD dwErrCode
);
DWORD WINAPI GetLastError(void);
一个控制加锁对象 传入参数CSpinGuard
原理:初始化时调用CSpinGuard的Lock函数 当析构时调用CSpinGuard的Unlock;
typedef CLocalLock CSpinLock;
template class CLocalLock
{
public:
CLocalLock(CLockObj& obj) : m_lock(obj) {m_lock.Lock();}
~CLocalLock() {m_lock.Unlock();}
private:
CLockObj& m_lock;
};
是一个锁
原理:一直放弃CPU时间实现加锁
class CSpinGuard
{
public:
CSpinGuard() : m_lFlag(0)
{
}
~CSpinGuard()
{
ASSERT(m_lFlag == 0);
}
void Lock()
{
for(UINT i = 0; !TryLock(); ++i)
YieldThread(i);
}
BOOL TryLock()
{
if(::InterlockedCompareExchange(&m_lFlag, 1, 0) == 0)
{
//如多个线程操作同一个内存地址的情形,刚好有线程把内存里的数读入寄存器,想在寄存器里做完计算完后再写回内存,这就很有可能造成寄存器里数没有及时写回内存,下一个线程再操作时可能会造成数据不一致,而_ReadWriteBarrier可以强制完成内存的读写。
::_ReadWriteBarrier();
return TRUE;
}
return FALSE;
}
void Unlock()
{
ASSERT(m_lFlag == 1);
m_lFlag = 0;
}
private:
CSpinGuard(const CSpinGuard& cs);
CSpinGuard operator = (const CSpinGuard& cs);
private:
volatile LONG m_lFlag;
};
返回*Destination的原始值。
当Comparand 和*Destination相等时,*Destination的值设为Exchange 一样。否则,*Destination值不变。
LONG InterlockedCompareExchange(
_Inout_ LONG volatile * Destination,//指向输入值的指针,和Comparand的值比较。
_In_ LONG Exchange,//如果Destination指针指向的值和Comparand的值一样,就把Destination指针指向的值换成Exchange的值。
_In_ LONG Comparand//指定与Destination 指针指向值比较的值。
);
如多个线程操作同一个内存地址的情形,刚好有线程把内存里的数读入寄存器,想在寄存器里做完计算完后再写回内存,这就很有可能造成寄存器里数没有及时写回内存,下一个线程再操作时可能会造成数据不一致,而_ReadWriteBarrier可以强制完成内存的读写。
static inline void YieldThread(UINT i = DEFAULT_PAUSE_RETRY)
{
if (i < DEFAULT_PAUSE_RETRY) ;
else if (i < DEFAULT_PAUSE_YIELD) YieldProcessor();
else if (i < DEFAULT_PAUSE_CYCLE - 1) SwitchToThread();
else if (i < DEFAULT_PAUSE_CYCLE) Sleep(0);
else YieldThread(i & (DEFAULT_PAUSE_CYCLE - 1));
}
超线程处理器使用YieldProcessor 宏让出CPU,如果CPU不支持,这个宏为空。
这个操作的切换时间比较小,效率高。这个宏在winnt.h中定义:
void YieldProcessor(void);
Sleep(0):时间片只能让给优先级相同或更高的线程;
SwitchToThread():只要有可调度线程,即便优先级较低,也会让其调度。
其中2003 server开始,Sleep(0)变成了调度所有可调度线程,跟SwitchToThread差不多了。
C++的线程函数内调用了一个自定义的yield()接口,在windows上是调用了SwitchToThread,linux是pthread_yield。
https://blog.csdn.net/tjunxin/article/details/8540259
如文中所述,特需注意类或结构体中包含STL模板(Vector、List、Map等等),那么使用ZeroMemory对这个类的对象中进行清零操作也会引起一系列的崩溃问题(指针指向内存错误、迭代器越界访问等)。所以,再次强烈建议:类(class)只使用构造函数进行初始化,不要调用ZeroMemory进行清零操作。
class Base
{
public:
bool _p;
std::map _map;
Base()
{
int nSize = sizeof(_map);//nSize=20
::ZeroMemory(this, sizeof(Base));
}
};
int main()
{
Base hxtest;
hxtest._map[10] = 10;//这个位置迭代器访问出错
hxtest._map[1] = 1;
int nSize = sizeof(hxtest);//24
return 0;
}
继续跟代码
返回首次出现_Val的位置的指针,返回的地址是被查找字符串指针开始的第一个与Val相同字符的指针,如果Str中不存在Val则返回NULL。
注意如果strchr与其他c函数一样是以‘\0‘表示查找结束
#include
#include
int main()
{
const int nSize = 5;
auto p = new char[nSize];
int nIndex = 0;
for (; nIndex < nSize; ++nIndex)
{
p[nIndex] = '0' + nIndex;
std::cout << p[nIndex] << " ";
}
p[nIndex] = '\0';
int nLength = strlen(p);
p[6] = '6';
auto p2 = strchr(p, '6');//p2==nullptr 如果注释p[nIndex] = '\0',那么p2!=nullptr
return 0;
}
这个函数的主要作用是将若干个argument按照format格式存到buffer中。
返回值:写入的字符数或-1,如果出现错误。 如果缓冲区或格式是 null 指针, sprintf_s和swprintf_s返回-1 并设置errno到EINVAL。
sprintf_s返回存储中的字节数缓冲区,不包括终止 null 字符。 swprintf_s返回的存储中的宽字符数缓冲区,不包括终止 null 宽字符。
tchar版本:_stprintf_s
Unicode:swprintf_s
多字节:sprintf_s
int _stprintf_s( char *buffer, //输出的字符
size_t sizeOfBuffer, //buffer的长度
const char *format //格式字符串,比如%s
[,argument] ... );//可选参数
这个函数的主要作用是从buffer中读取指定格式(format)的字符到相应的argument中。与_stprintf_s作用相反
tchar版本:_stscanf_s
Unicode:swscanf_s
多字节:sscanf_s