多线程进行n皇后计算
在浏览zhihu的时候, 看到了这个问题:Linux c++服务器端这条线怎么走? http://www.zhihu.com/question/22608820 , 其中排第一的答案说的很不错。针对他结尾处给出的问题,一直想自己做一下,最近工作不忙,也写了以下,还是发现了一些自己存在的问题,总结一下吧。
一个简单的8皇后问题可以比较容易的实现
#include <string.h>
#include <iostream>
#include <stdlib.h> int cntTotal = ;
int BOARDSIZE=;
bool markboard(bool* b, int row, int col){
for(int i =row;i<BOARDSIZE;++i){
*(b+i*BOARDSIZE+col) = true;
}
for (int i=row+, j=col- ; i<BOARDSIZE && j>=; ++i, --j)
*(b+i*BOARDSIZE+j)=true;
for(int i = row+, j=col+; i<BOARDSIZE && j<BOARDSIZE; ++i, ++j)
*(b+i*BOARDSIZE+j)=true; return true;
}
bool IsQuerePos(bool* board, int idx){ bool *b = new bool[BOARDSIZE*BOARDSIZE];
for(int i = ;i<BOARDSIZE; ++i){
if(*(board+idx*BOARDSIZE+i) )
continue;
if(idx == BOARDSIZE-){
cntTotal ++;
std::cout<<cntTotal<<"\r";
}
else{
memcpy(b, board, sizeof(bool)* BOARDSIZE*BOARDSIZE); markboard(b, idx, i);
IsQuerePos(b, idx+);
}
}
delete b;
} int main(int argc, char** argv){
if(argc ==)
BOARDSIZE = atoi(argv[]);
std::cout<< argc << std::endl;
bool *board = new bool[BOARDSIZE*BOARDSIZE];
bool *p = board;
for(int i = ; i<BOARDSIZE; i++){
for(int j=; j<BOARDSIZE;j++)
*p++ = false;
}
IsQuerePos(board, );
std::cout<<cntTotal<<std::endl;
delete board;
}
运行起来是没有任何问题的。然后就是想办法改成多线程的版本。对于多线程版本,最容易的方案就是从第一行开始,每个线程计算不同的列,也就是将n皇后问题,分成n个子任务,这样每个子任务互不影响,可以进行并行计算。这样子的算法是我们人为的来进行子任务的分解,简单,线程之间不需要同步,易于实现。更复杂的算法是由算法智能调度各线程的负载,我没学过并行计算,复杂的就不研究了,实现简单的就行了。
核心代码如下:
class ThreadMgr{
public:
ThreadMgr(int boardSize, int threadCount): m_board_size(boardSize), m_thread_count(threadCount){
} void Start(){
m_threads = new Thread*[m_thread_count];
for (int i = ;i<m_thread_count; ++i)
{
m_threads[i] = new Thread();
m_threads[i]->Start();
}
{
// PerformanceHelper ph("internal-");
QueueEight** qes = new QueueEight*[m_board_size];
int cnt = ;
for(int i=;i<m_board_size; ++i){
qes[i] = new QueueEight(m_board_size);
m_threads[i%m_thread_count]->PostJob(new CJob2<QueueEight, int, int>(qes[i], &QueueEight::PlaceQueueOnPos, , i));
//qes[i]->PlaceQueueOnPosInternal(0, i, cnt);
} for (int i = ;i<m_thread_count; ++i)
{
m_threads[i]->Finish();
}
} std::cout<< QueueEightDelegate::getCnt() <<std::endl; } private:
int m_thread_count;
Thread** m_threads;
int m_board_size;
};
其中Thread是我封装的一个线程类,参照chrome的线程模型;CJob2对应了chrome的task,就是一个独立的任务,可以交给线程Thread来执行;为了线程之间的数据独立,定义了一个QueueEight的类来完成n皇后的计算,代码如下:
class QueueEight{
public:
QueueEight():m_board(NULL), m_board_size(8){ } QueueEight(int size):m_board_size(size){
m_board = new bool[m_board_size*m_board_size];
for( int i =0;i<m_board_size; ++i){
for(int j=0; j<m_board_size; ++j){
m_board[i*m_board_size + j] = false;
}
}
} QueueEight(const QueueEight* qe){
m_board_size = qe->m_board_size;
m_board = new bool[m_board_size*m_board_size];
memcpy(m_board, qe->m_board, sizeof(bool)* m_board_size*m_board_size);
} ~QueueEight(){
delete[] m_board;
} void PlaceQueueOnPos(int row, int col){
int cnt =0;
{
//PerformanceHelper ph("abc");
PlaceQueueOnPosInternal(row, col, cnt);
}
QueueEightDelegate::OnCalcComplete(cnt);
} int PlaceQueueOnPosInternal(int row, int col, int& cnt){
if(row == m_board_size-1){
cnt ++;
}
else{
*(m_board+row*m_board_size+col) = true;
QueueEight qe(this);
qe.markboard(row, col);
for (int i = 0;i<m_board_size;++i)
{
if (qe.QueueExist(row+1, i))
continue;
qe.PlaceQueueOnPosInternal(row+1, i, cnt);
}
*(m_board+row*m_board_size+col) = false;
}
return cnt;
} bool inline QueueExist(int row, int col){
return *(m_board + row*m_board_size + col);
} private:
bool markboard( int row, int col){
for(int i =row;i<m_board_size;++i){
*(m_board+i*m_board_size+col) = true;
}
for (int i=row+1, j=col-1 ; i<m_board_size && j>=0; ++i, --j)
*(m_board+i*m_board_size+j)=true;
for(int i = row+1, j=col+1; i<m_board_size && j<m_board_size; ++i, ++j)
*(m_board+i*m_board_size+j)=true;
return true;
} private:
bool* m_board;
int m_board_size;
};
这个类实现n 皇后的递归回朔算法,为了回朔方便,计算下一层的时候,重新new 一个QueueEight 对象,撤销时直接delete就行了。一看没有问题吧,然后计算结果也是正确的,但是运行时发现了一个问题,我用1个线程和4个线程来运行时,总时间上并没有什么差别,我的机器是i7 的8核,所以4个线程不会有大问题,那么问题哪儿呢?
查了一些资料感觉找不出问题,所以借助于工具吧。vs2010带的performance profile工具,可以帮我们看看线程运行时的状态:
通过这个,就可以收集线程运行的一些性能信息,通过分析发现,在new 新的 QueueEight对象时,会触发到默认堆的锁,而多线程同时new时,冲突会加剧,导致delay。突然想起以前接触tcmalloc时,核心思想就是线程分配 小对象时,从线程的私有内存分配,避免不同的线程共享堆而导致的加锁现象。问题找到了后,开始优化,将QueueEight的算法改一下。一开始就申请n个board区域,分别存储不同的行位置时queue的位置,方便回朔。这样在递归时就不需要分配内存了。实现代码如下:
class QueueEight{
public:
QueueEight():m_board(NULL), m_board_size(){ } QueueEight(int size):m_board_size(size){
m_board = new bool*[m_board_size];
for (int i =;i<m_board_size;++i)
{
m_board[i] = new bool[m_board_size*m_board_size];
} for( int i =;i<m_board_size; ++i){
for(int j=; j<m_board_size; ++j){
m_board[][i*m_board_size + j] = false;
}
}
} ~QueueEight(){
for (int i =;i<m_board_size;++i)
{
delete[] m_board[i];
}
delete[] m_board;
} void PlaceQueueOnPos(int row, int col){
int cnt =;
{
PerformanceHelper ph("abc");
PlaceQueueOnPosInternal(row, col, cnt);
}
QueueEightDelegate::OnCalcComplete(cnt);
} int PlaceQueueOnPosInternal(int row, int col, int& cnt){
if(row == m_board_size-){
cnt ++;
}
else{
*(m_board[row]+row*m_board_size+col) = true; memcpy(m_board[row+], m_board[row], sizeof(bool)*m_board_size*m_board_size);
markboard(m_board[row+], row, col);
for (int i = ;i<m_board_size;++i)
{
if (QueueExist(m_board[row+], row+, i))
continue;
PlaceQueueOnPosInternal(row+, i, cnt);
}
*(m_board[row]+row*m_board_size+col) = false;
}
return cnt;
} bool inline QueueExist(bool* board, int row, int col){
return *(board + row*m_board_size + col);
} private:
bool markboard(bool*board, int row, int col){
for(int i =row;i<m_board_size;++i){
*(board+i*m_board_size+col) = true;
}
for (int i=row+, j=col- ; i<m_board_size && j>=; ++i, --j)
*(board+i*m_board_size+j)=true;
for(int i = row+, j=col+; i<m_board_size && j<m_board_size; ++i, ++j)
*(board+i*m_board_size+j)=true;
return true;
} private:
bool** m_board;
int m_board_size;
};
运行后,发现能够缩短总的运行时间。 15 皇后 4线程 运行3s, 15皇后 1线程 运行10s
其实我门还可以在进行优化,以位图来存储,而不用bool存储,也可以进行调度算法的设计和优化,但是线程之间如果同步的需求增多时,那么性能也会下降,这个我不太懂,可能要学习后才能搞搞。
通过这个问题,学习了一下多线程的性能分析,学会了用vs2010怎么分析程序的性能;对多线程的同步有了更深的理解;对多线程处理时数据的隔离也有了了解,所以
mark以下.
多线程进行n皇后计算的更多相关文章
- 8皇后以及N皇后算法探究,回溯算法的JAVA实现,非递归,循环控制及其优化
上两篇博客 8皇后以及N皇后算法探究,回溯算法的JAVA实现,递归方案 8皇后以及N皇后算法探究,回溯算法的JAVA实现,非递归,数据结构“栈”实现 研究了递归方法实现回溯,解决N皇后问题,下面我们来 ...
- 海量数据相似度计算之simhash短文本查找
在前一篇文章 <海量数据相似度计算之simhash和海明距离> 介绍了simhash的原理,大家应该感觉到了算法的魅力.但是随着业务的增长 simhash的数据也会暴增,如果一天100w, ...
- WPF多线程UI更新——两种方法
WPF多线程UI更新——两种方法 前言 在WPF中,在使用多线程在后台进行计算限制的异步操作的时候,如果在后台线程中对UI进行了修改,则会出现一个错误:(调用线程无法访问此对象,因为另一个线程拥有该对 ...
- 有一个很大的整数list,需要求这个list中所有整数的和,写一个可以充分利用多核CPU的代码,来计算结果(转)
引用 前几天在网上看到一个淘宝的面试题:有一个很大的整数list,需要求这个list中所有整数的和,写一个可以充分利用多核CPU的代码,来计算结果.一:分析题目 从题中可以看到“很大的List”以及“ ...
- 多线程/多进程/异步IO
SOCK_STREAM :TCPSOCK_Dgram :UDP family=AF_INET: 服务器之间的通信AF_INET6: 服务器之间的通信AF_UNIX: Unix不同进程间的通信 永远遵循 ...
- python 多进程和多线程
在计算大量数据时,可以使用多进程 多线程机制来加速计算 多进程 import multiprocessing import os def run_proc(name): print('Child pr ...
- WPF多线程UI更新
前言 在WPF中,在使用多线程在后台进行计算限制的异步操作的时候,如果在后台线程中对UI进行了修改,则会出现一个错误:(调用线程无法访问此对象,因为另一个线程拥有该对象.)这是很常见的一个错误,一不小 ...
- 并发编程~~~多线程~~~守护线程, 互斥锁, 死锁现象与递归锁, 信号量 (Semaphore), GIL全局解释器锁
一 守护线程 from threading import Thread import time def foo(): print(123) time.sleep(1) print('end123') ...
- Python多进程和多线程是鸡肋嘛?【转】
GIL是什么 Python的代码执行由 Python虚拟机(也叫解释器主循环,CPython版本)来控制,Python在设计之初就考虑到在解释器的主循环中,同时只有一个线程在运行.即每个CPU在任意时 ...
随机推荐
- MySQL Online DDL 工具之pt-online-schema-change
MySQL DDL:DDL是一个令所有MySQL dDBA 诟病的一个功能,因为在MySQL中在对表进行dDDL时,会锁表,当表比较小比如小于1W行时,对前端影响较小,当时遇到千万级别的表,就会影响前 ...
- UIActivityIndicatorView的使用
class ViewController: UIViewController,UIActionSheetDelegate{ @IBOutlet weak var label1: UILabel! @I ...
- ASP.NET MVC NonActionAttribute使用说明
默认情况下,MVC 框架将 controller 类的所有公共方法都视为操作方法. 如果您的 controller 类包含公共方法,并且您不希望它成为操作方法,则必须用 NonActionAttrib ...
- OC self super isa指针
self指针: self是oc面向对象设计中的一个特殊指针,相当于java中的this,但是比this强大,this只能访问实例对象的相关方法和成员变量,或者说this只代表实例对象: self不仅可 ...
- 玩转SmartQQ之登录
SmartQQ是腾讯新出的一个WebQQ,登录地址是:http://w.qq.com/,目前之前的WebQQ可以继续使用,登录地址:http://web2.qq.com/webqq.html,Smar ...
- android开发之socket快传文件以及消息返回
应用场景: 两台android机器:一台自建wifi热点,另一台搜索到,连接该wifi热点.之后再通过socket传递消息,文件等,当服务器端接收到消息之后会返回对应的应答消息: 注意点:接收到消息之 ...
- PythonChallenge 2:爬虫和正则表达式
题目: 解题思路:题目里已经说的很清楚了,字符可能在网页的源代码里.右键查看网页源代码,发现其中有一段:find rare characters in the mess below.有些人是直接把下面 ...
- 3157: 国王奇遇记 & 3516: 国王奇遇记加强版 - BZOJ
果然我数学不行啊,题解君: http://www.cnblogs.com/zhuohan123/p/3726933.html const h=; var fac,facinv,powm,s:..]of ...
- IOS7 适配时导航栏变黑
当适配IOS的布局时遇到问题:导航栏和菜单栏后台会变黑色. self.edgesForExtendedLayout = UIRectEdgeNone; 原因是系统默认这两个控件是半通明的. 解决方案: ...
- 新建android项目src和layout文件夹中没有内容的问题
这个问题好像是最新版ADT(ver:23.0.0)才会出现的问题,解决办法也简单,直接把android SDK和ADT的老版本(ver:22.6.2)覆盖安装一次就好了.至于新版为什么会这么设计,现在 ...