最近要批量整理一大堆BAM数据,批量Map到转录组固定位点。写了一个单线程的程序,估计了一下,跑完估计得2天,完全等不了。所以开始写准备写perl语言的多线程,经过半个下午的折腾,终于找到了靠谱的方法。感叹一下,perl是不是已经被抛弃了,现在真心是python的天下。
-
项目的多线程需求
-
多线程模块Parallel::ForkManager
-
血泪史
-
我对Perl语言的理解
我现在把文件压缩成bam格式并且index,现在需要把reads的位点定位在转录本的固定位点上。也就是这是一个单线程不怎么耗时的任务,需要用大量多线程并发来提高效率。
所以单线程任务为:
-
调用用samtools提取转录本所在的位置的所有reads
-
用perl处理后在数组固定位置加1
-
将数组输出到文件
每个单线程耗时很短,最耗时的可能是调用samtools,不过因为bam我都进行index了,几乎秒查。
如果需要多线程,那么需求为:
-
同时提交多个单线程任务,线程之间数据不共享
-
数据输出到文件的时候,多线程不能冲突(比如多个线程同时往一个文件中数据,那结果就乱了)
-
最好能控制一下线程数量,不能一次提交全部线程。
人生苦短,我用Parallel::ForkManager,不用threads:)
这个模块优点就是,CPAN上有详细的API,作者也给出了多个示例供参考,几行代码轻松实现多线程=-=,这就是我需要的模块。
我用CPAN安装
cpan Parallel::ForkManager
对于国内的朋友需要改一下源:
'urllist' => [q[http://mirrors.163.com/cpan/]]
$ cpan
Loading internal logger. Log::Log4perl recommended for better logging
Terminal does not support AddHistory.
To fix that, maybe try> install Term::ReadLine::Perl
cpan shell -- CPAN exploration and modules installation (v2.27)
Enter 'h' for help.
cpan[1]> o conf
$CPAN::Config options from /home/xxx/.cpan/CPAN/MyConfig.pm:
commit [Commit changes to disk]
defaults [Reload defaults from disk]
help [Short help about 'o conf' usage]
o conf urllist q[http://mirrors.163.com/cpan/]
装好之后,可以先use测试一下
use Parallel::ForkManager
如果报错的话,仔细看错误,我在装的时候提示还有一个包没有装(如果是路径不对就自行调整),Moo::Role。
cpan Moo::Role
之后就能运行啦~
我对于多线程理解的语法应该类似于这样的:
$最大线程数=20;
for my $i(@你的所有任务){
$开始多线程
单线程任务(一些参数)
$结束多线程
sub 单线程任务{
这里先把所有线程安排到一个“等待队列”里面,同时有一个有限长度的“工作队列”。完成一个,出“工作队列”,同时从“等待队列”取出一个加入“工作队列”保持最大“工作队列长度”。
或者是没有“等待队列”,"工作队列"有空缺了,再生成一个任务补进去。这种听起来似乎更节约内存。
虽然不知道FolkManager究竟的内部逻辑是什么,但是语法逻辑和上面的一样,很便于理解。
use strict;use warnings;
use Parallel::ForkManager;
my $pm=Parallel::ForkManager->new(20)
my @jobs;
for my $i(@jobs){
$pm ->start and next ;
这里写上你想让多线程做的事情
$pm ->finish;
$pm->wait_all_children;
完美!
刚才提到了还有一个问题,就是多线程会一起输出。如果几个线程同时写文件就会出现错乱,如果写到不同的文件倒是一个方案,但是最后cat的压力就比较大,我这个共几千万个转录本,最后就是几千万个文件的cat,实在崩溃。
我发现添加下面的语句,就不会出现错乱问题。就是设置在子线程计算完成的时候,都要运行这个程序,统一输出,吧......我也不知道为什么,但是,it work!
my $pm=Parallel::ForkManager->new(20)
$pm -> run_on_finish(
sub {
my ($pid, $exit_code, $ident, $exit_signal, $core_dump, $data_structure_reference) = @_;
if (defined($data_structure_reference)){
my $string = ${$data_structure_reference};
print $string,"\n";