帖子

目前显示的是 2016的博文

Windows程序在实现线程池时要注意Overlapped IO是否完成

windows有个限制,异步IO操作是绑定在发起IO请求的线程上的。一旦发起者线程退出,那么这个IO操作也就会被取消,而且没有callback会被调用。

MSDN中的WSASend函数的文档中有这么一段话:
"All I/O initiated by a given thread is canceled when that thread exits. For overlapped sockets, pending asynchronous operations can fail if the thread is closed before the operations complete. "

所以在调用WSASend方法时,务必要清楚自己在什么样的线程里。假如是在一个线程池中,并且这个线程池的线程数是可以动态增减的,那么就要小心了。万一你刚Send完,这个线程就exit了,那么就惨惨惨了!这种bug非常非常难以debug出来。

解决办法:
方案1.  ThreadPool的实现者通过GetThreadIOPendingFlag函数来得知是否可以安全退出
方案2.  所有的异步IO操作都先放到一个queue里,然后由特定的线程来处理这个queue

我刚发信问过了windows kernel team负责threadpool的人,他回答说windows自带的thread pool api考虑到了这一点,他在信中说:“Threadpool will not release threads while they have pending IRPs in them。This means your IO will complete even if it completes after you return the thread back to the Threadpool.” 同样的,.net 程序也是如此。

但是,我所见过的所有使用IOCP的程序,几乎全部都是自己制作的thread pool,没有使用操作系统的thread pool API。这些thread pool能实现干干净净shutdown的寥寥无几。




Windows上如何在进程间传递socket句柄

问题的背景是这样的:我需要写一个程序A,来launch另一个程序B。B是一个server程序,它需要监听一个TCP端口。这个端口是A来分配的,A需要确保这个端口没有被占用。

我最初的办法是这样:写一个函数,遍历现在的tcp table,来判断哪些端口已经在使用中。

std::vector<int> getFreePorts(int begin, int end) { char errbuf[256]; if (end <= begin) { THROW_EXCEPTION("illegal argument"); } std::unique_ptr<MIB_TCPTABLE2, decltype(std::free)*> pTcpTable(nullptr, std::free); ULONG ulSize = sizeof(MIB_TCPTABLE); for (int iter = 0; iter != 2; ++iter) { pTcpTable.reset((MIB_TCPTABLE2*)malloc(ulSize)); ULONG dwRetVal = GetTcpTable2(pTcpTable.get(), &ulSize, FALSE); if (dwRetVal == NO_ERROR) break; if (dwRetVal != ERROR_INSUFFICIENT_BUFFER) { THROW_EXCEPTION("GetTcpTable2 failed,error = %ul", dwRetVal); } } std::vector<uint8_t> avail(end - begin, 1); for (int i = 0; i != pTcpTable->dwNumEntries; ++i) { auto& table = pTcpTable->table[i]; int off = table.dwL…

进程内的生产者/消费者队列,一点思考和总结

考虑这样一个实际场景:
有一个文本文件,需要根据每一行的内容计算出一个score来。将score输出到一个新的文件。假设计算score是很耗费cpu的,请尽可能用多线程并行化的方式来加速。

这时我们会想到map-reduce/work-stealing/bounded queue等等。

拿bounded queue来说,最基本的版本是一个mutex两个条件变量,高级的版本是两个mutex两个条件变量。

接下来要解决的问题是:当生产者读到文件末尾时,如何把这件事情告诉消费者? 假设只有一个生产者,有多个消费者。一种简单的做法是:如果消费者的数量固定,已知,假设有n个,那么就往队列里扔n个特殊类型的消息以标记EOF。当消费者读到EOF则退出。这个思路也可扩展到多个生产者、多个消费者的情况。这是一种纯message-passing的方案。

但是如何处理突发出错?假设生产者读文件的时候突然读到一条错误的记录,它想要立刻通知所有的消费者结束,该怎么办?如果我们的队列是支持优先级的,那么把control message设置成最高优先级,这个问题也可以解决。但如果是反过来,消费者报错,希望所有的生产者和消费者都立刻终止,该怎么办?如果我们有一个双向的queue,这个问题也可以解决。但是往往最简单的方案还是加入一个coordinator,让它具有一个isHealthy()这样的方法。此时,所有条件变量的wait方法都需要改成timed wait。

再看另一个与此很相关的问题:假设我们有n个task,要交给n个线程同时执行。然后主线程需要等待并搜集这n个结果。这个看似很简单,可以用count down latch实现一个栅栏,也可以创建n个feature 然后挨个wait。但是假如要考虑出错的情况,即,子线程在执行的时候可能会遇错提前退出,此时希望其它的线程也尽快停止。

这时候最简单的办法是:让count down latch有一个countDownToZero这样的方法。这样主线程就收到错误直接停止等待了。还有一种办法就是把task result及task id都放入一个unbounded queue中,然后主线程一直wait在这个queue上。

但无论如何,为了能安全且及时的通知其它线程退出,无论什么样的场景,代码中不能有无限时间的wait。所有的wait必须得带有一个时间间隔…

如何在VC++中强制引用静态库里的所有全局变量

图片
这个坑啊…… 真是历史悠久名声昭著了。

假如你的exe用到了一个静态库,而这个静态库有一些全局变量。那么这些全局变量未必会被引用到这个exe当中。

举个例子:
Foo.h:
===================
class Foo
{
public:
Foo();
};
===================
Foo.cpp: =================== #include "Foo.h" #include <stdio.h>

Foo::Foo() { printf("hello"); }
static Foo foo; =================== 假如Foo.cpp是被编译成静态库,那么链接的时候可能没有foo这个变量,从而也不会有"hello"输出。

有些人写代码喜欢用全局变量来实现singleton,并且期望这些singleton的构造函数会在main函数之前被调用。呵呵…… 到了vc这里就不灵了。

可是,如果万一他的代码就是这样,你又没法改,办法还是有的。
办法1:链接的时候加 /WHOLEARCHIVE 。这是VS 2015 update 2以后才有的选项办法2:
1. 要以项目引用的方式来引用静态库,而不是把.lib文件作为input给linker
2. 把“link library dependencies” 和 "Use link library dependency inputs” 这两个选项设置成true。





如何正确的关闭已打开的文件

如果一个文件是以只读的方式打开的,那么忘记关闭的后果是:句柄泄露。通常来说这不是太大的问题。
如果一个文件是以可写的方式打开的,那么忘记关闭的后果是:除了句柄泄露以外,写进去的东西可能会丢失。并且,如果关闭了,但是忘记检查close(或fclose)函数的返回值,也会有丢数据的风险。

要注意点什么? 写文件的时候,要手动关,不要依赖于析构函数。用RAII来管理IO资源是非常愚蠢的。如果fclose出现在析构函数里,那么通常就是一个错误。因为析构函数不能抛异常,如果fclose返回非0的值,你很难把这个错误报告出去。出错不可怕,最可怕的是出错了但是你却不知道。比如,你的程序每天自动生成一个config文件然后自动推送到线上每台机器。结果有一天,生出来的config文件缺了一块,但是你却不知道,而这个config却被推送出去了。

什么时候fclose会返回非0值? 比如,硬盘满了。再比如,有些机器的硬盘会有一些临时故障,会导致写入偶尔失败。而这种临时故障不一定会产生报警。其实我们希望一旦有这样的问题发生,这些机器能赶紧被应用程序发现,然后从系统中剔除出去。
正确的该怎么写? FILE* fd = fopen("xxx.txt","w"); if(!fd) return; try{   //do normal io stuffs    ...  }catch(std::exception& ex){    if(fclose(fd)){       //....?    } } if(fclose(fd)) throw std::runtime_error("err");
问题来了,如果我们想把fclose失败这件事情报告出去,就得在处理异常的时候又抛出新的异常。该怎么办?遗憾的是,C++中对于这样的问题,并没有标准的做法。

Java是怎么处理这样的问题? Java7为此特地推出了Suppressed Exceptions. 比如下面的代码: package testjava; import java.io.Closeable; import java.io.IOException; public class T1 { static class A implements Closeable{ @…

人民币与IMF特别提款权(SDR)

图片
最近关于人民币被纳入IMF的特别提款权的讨论很热,我认同这个观点:“它的象征意义大于实际意义。”

SDR是布雷顿森林体系的产物,在2009年以前从未发挥过实际作用。在布雷顿森林体系时代,各国央行为了维持本国货币与美元之间的固定利率,不得不储备大量美元。于是SDR就被设计出来,作为外汇储备的一种。可惜它还尚未发挥实际作用,固定汇率制度就倒台了,于是SDR也就被尘封起来。然而若干年后,中国崛起,中国央行长久以来选择了人民币和美元之间的固定汇率制度。如下图:

为了干预汇率以及强制结汇,中国央行不得不储备大量的美元,代价是每年得为此付出高昂的成本。2008年之后,美元大幅贬值,但中国央行继续增加美元外汇储备大量购买美国国债,于是遭到很多质疑。于是央行行长周小川就瞄准了SDR。2009年3月,周小川在中国人民银行网站上发文《关于改革国际货币体系的思考》,文中建议“创造一种与主权国家脱钩、并能保持币值长期稳定的国际储备货币,从而避免主权信用货币作为储备货币的内在缺陷”。这种货币指的就是SDR。它希望在外汇储备资产中,增加SDR的比重,减少美元,以降低美国对中国的货币经济影响。

但SDR不是你想买就能买的,它的发行量非常小。当时SDR在全球的发行量只有20亿美元,中国拥有4%,大约8千万,和其上万亿的外汇储备相比不值得一提。为了提高SDR在外汇储备中的占比,中国做了两件事情:
推动IMF在2009年对SDR做了一次大规模增发,使其发行量一次性扩大10倍左右。要求增缴份额。使其在IMF份额从3.996%升至 6.394%,投票权随之增加。其SDR allocation在将来也会随之同比扩大。要求把RMB纳入SDR的货币篮子当中 第一条,不需要美国国会批准。而对于后两点,美国是极力反对的。所以这件事情从2010年被提出,一直拖到了2015年底,才终于在美国国会批准。可以说,不是IMF批准人民币“入篮”SDR,而是美国国会批准了此事。因为美国对IMF具有绝对的控制权,它在IMF中有权否决一切提议。
截至2016年6月30日,中国央行的外汇储备资产中包括:3万亿的外汇储备、800亿的货币黄金和100亿的SDR。可见SDR的地位可忽略不计,更何况SDR的流动性远远不能和美国国债相比。
有观点说,SDR作为一种外汇储备资产,可被列入IMF 100多个成员国的资产负债表上。而RMB作为SDR的…

How to removes duplicate values from an array: A counterintuitive story of Big O

Everyone knows O(N) is faster than O(NlogN). However, sometimes, it isn't.
Please look at this example:

template <typename T>
size_t count_unique_elements_by_map(T* begin, T* end) {
  std::unordered_set<T> s(end - begin);
  s.insert(begin, end);
  return s.size();
}

template <typename T>
size_t count_unique_elements_by_sort(T* begin, T* end) {
  if (begin >= end) return 0;
  std::sort(begin, end);
  auto newEnd = std::unique(begin,end);
  return newEnd - begin;
}

Method 2 is faster than method 1 if duplicates are rare.
问题的背景是这样:我有一些ID,我想把这些ID对应的value查出来。这些value保存在一个分布式系统中(如memcached)。为了减少服务器的查询压力,我把这些ID先去重,然后计算每个ID在哪个服务器上,把ID按服务器分组,然后发送查询。最初我用了一个hash map做这件事情,然后我发现去重的操作所耗费的CPU远远超过其它代码(比如网络IO)。

然后我发现sort比hash更快,在我的场景下,快10倍以上。但是故事并未到此结束,当我收到服务器的reply之后,我还是得把这些ID以及value插入到一个hash map中。否则我就只能用binary search读取它们。所以,我还是得构建这个hash map,逃不掉的。

再后来我发现,如果把STL的hash map换成我自己写的一个基于open address方式的hash map,那么速度也能提升1倍左右。

Please tell me if you have any better solutions. Thanks.

upd…

Torch的tensor

Torch中的tensor是一个n维数组,它是torch最基础最重要的组件。
下面这段代码演示如何通过下标来访问tensor。这段代码首先,初始化一个长度为10的1维数组,将其内容填充成0,1,2...9,然后遍历这个tensor,将其内容打印出来。
#include "TH/TH.h" #define Real Double #define real double int main() { THTensor* tensor1 = THTensor_(newWithSize1d)(10); for (int64_t i = 0; i != 10; ++i) { THTensor_(set1d)(tensor1, i, (real)i); } for (int i = 0; i != 10; ++i) { printf("%g ", THTensor_(get1d)(tensor1, i)); } printf("\n"); THTensor_(free)(tensor1); return 0; } 输出应该是:
0 1 2 3 4 5 6 7 8 9
使用下标来访问tensor有两种方式,上面这种是通过set/get函数,比较慢。更快的方式是这样。
#include "TH/TH.h" #define Real Double #define real double int main() { THTensor* tensor1 = THTensor_(newWithSize1d)(10); int i = 0; TH_TENSOR_APPLY(real, tensor1, *tensor1_data = (real)i; ++i;); for (int i = 0; i != 10; ++i) { printf("%g ", THTensor_(get1d)(tensor1, i)); } printf("\n"); THTensor_(free)(tensor1); return 0; } TH_TENSOR_APPLY是一个宏,在TH/THTensorApply.h中声…

少写代码,多读别人写的代码

我想给我的电脑前贴个条子:“少写代码,多读别人写的代码”
想要成为一个优秀的程序员真的很不容易。
不是说天天读英语听英语,英语水平就会好。只有在刻意学习的时候,才会有收获。
同样的,不是说天天专心写代码,coding能力就会渐涨。除非刻意的想要提高自己,否则,不出几年就会达到自身的瓶颈。
我才多大啊,比我老练的程序员多了去了,如大海里的沙子。
要天天牢记自己是个渣滓,督促自己少写代码,只写必要的。因为写了就得有人维护,就算自己不嫌累,也不要给别人添麻烦添负担。