Here is one possible solution:
use strict;
use threads;
use Thread::Queue;
my $queue = Thread::Queue->new();
my @threads;
my $maxthread = 5; #how many threads are you want
push @threads, threads->create(\&worker) for 1 .. $maxthread;
if(exists $ddts_attachments->{$id}->{'urls'}){
foreach my $url(sort keys %{ $ddts_attachments->{$id}->{'urls'} }){
$queue->enqueue($url);
}
$queue->enqueue(undef) for 1 .. $maxthread; #no more data to process
}
#wait here until all worker finish
$_->join for @threads;
sub worker {
while (defined(my $url = $queue->dequeue)) {
my $tid = threads->tid;
print "Thread $tid got $url\n";
#download and store the url
local $ENV{HTTP_proxy} = $proxy_url;
my $ff = File::Fetch->new(uri => $url);
my $where = $ff->fetch(to => "/attachments5/$id/");
my $file = $ff->file;
print "Thread $tid url: $file attached to $id key \n ......\n";
}
}