学习HP-Socket

https://www.oschina.net/p/hp-socket

Day01:

跟一遍代码

打开Windows的Demo TestEcho-UDP

设置与获取错误信息 

根据《Windows核心编程》错误信息可以自己注册 可以在vs中添加监视@err,hr 获得实时错误信息

void WINAPI SetLastError(
  _In_ DWORD dwErrCode
);
DWORD WINAPI GetLastError(void);

获得当前函数名__FUNCTION__

CSpinLock

一个控制加锁对象 传入参数CSpinGuard  

原理:初始化时调用CSpinGuard的Lock函数 当析构时调用CSpinGuard的Unlock;

typedef CLocalLock<CSpinGuard>			CSpinLock;
template<class CLockObj> class CLocalLock
{
public:
	CLocalLock(CLockObj& obj) : m_lock(obj) {m_lock.Lock();}
	~CLocalLock() {m_lock.Unlock();}
private:
	CLockObj& m_lock;
};

CSpinGuard

是一个锁

原理:一直放弃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;
};

InterlockedCompareExchange

返回*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()

如多个线程操作同一个内存地址的情形,刚好有线程把内存里的数读入寄存器,想在寄存器里做完计算完后再写回内存,这就很有可能造成寄存器里数没有及时写回内存,下一个线程再操作时可能会造成数据不一致,而_ReadWriteBarrier可以强制完成内存的读写。

YieldThread   

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

超线程处理器使用YieldProcessor 宏让出CPU,如果CPU不支持,这个宏为空。

这个操作的切换时间比较小,效率高。这个宏在winnt.h中定义:

void YieldProcessor(void);

Sleep(0)与SwitchToThread()区别

Sleep(0):时间片只能让给优先级相同或更高的线程;

SwitchToThread():只要有可调度线程,即便优先级较低,也会让其调度。

其中2003 server开始,Sleep(0)变成了调度所有可调度线程,跟SwitchToThread差不多了。

C++的线程函数内调用了一个自定义的yield()接口,在windows上是调用了SwitchToThread,linux是pthread_yield。

ZeroMemory

https://blog.csdn.net/tjunxin/article/details/8540259

如文中所述,特需注意类或结构体中包含STL模板(Vector、List、Map等等),那么使用ZeroMemory对这个类的对象中进行清零操作也会引起一系列的崩溃问题(指针指向内存错误、迭代器越界访问等)。所以,再次强烈建议:类(class)只使用构造函数进行初始化,不要调用ZeroMemory进行清零操作。

class Base
{
	public:
	bool _p;
	std::map<int, int> _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;
}

Day02

继续跟代码

strchr

返回首次出现_Val的位置的指针,返回的地址是被查找字符串指针开始的第一个与Val相同字符的指针,如果Str中不存在Val则返回NULL

注意如果strchr与其他c函数一样是以‘\0‘表示查找结束

#include<string.h>
#include<stdio.h>
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;
}

_stprintf_s

这个函数的主要作用是将若干个argument按照format格式存到buffer中。

返回值:写入的字符数或-1,如果出现错误。 如果缓冲区格式是 null 指针, sprintf_sswprintf_s返回-1 并设置errnoEINVAL

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] ... );//可选参数

_stscanf_s

这个函数的主要作用是从buffer中读取指定格式(format)的字符到相应的argument中。与_stprintf_s作用相反

tchar版本:_stscanf_s

Unicode:swscanf_s

多字节:sscanf_s

猜你喜欢

转载自blog.csdn.net/sinat_36391009/article/details/82383836