Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » ALLEGRO_THREAD

This thread is locked; no one can reply to it. rss feed Print
ALLEGRO_THREAD
alex glez
Member #16,757
October 2017

Hello !. I have a problem with ALLEGRO_THREAD.
I want to save a lot of bitmaps on the hard drive and I would like to be able to
do it by running several threads while containing "al_save_bitmap" but
I only get them to run one after another and not two at a time ..
If you can help me I would really appreciate it. Thank you

------------------------------------------------------------------

static void * SUB(ALLEGRO_THREAD *hilo, void *arg)
{
al_save_bitmap(ARCHIVOS[num_elem], foto_final[arg]);
return (0);
}

//*************************************************

void GUARDAR_FOTO()
{


no_hilo: if (hilo1==1 && hilo2==1) { goto no_hilo; }

if (hilo1 == 0)
{
hilo1 = 1;
hilo[0] = al_create_thread(SUB, 0);
al_start_thread(hilo[0]);
al_destroy_thread(hilo[0]);
hilo1 = 0;
return;
}

if (hilo2 == 0)
{
hilo2 = 1;
hilo[1] = al_create_thread(SUB, 1);
al_start_thread(hilo[1]);
al_destroy_thread(hilo[1]);
hilo2 = 0;
return;
}

}

//**************************************** main ********
hilo1=0;
hilo2=0;

for (num_elem=0; num_elem < 100; num_elem++)
{
GUARDAR_FOTO();
}

Peter Hull
Member #1,136
March 2001

I can't really see how that code would work at all, but..

If it were me I would just split your list of 100 photos into two, and have one thread do the first 50 and one thread do the last 50, then use al_join_thread to wait for them to finish.

Does that help?

alex glez
Member #16,757
October 2017

Hello, thanks for answering, because of the complexity of the program it is impossible for me to separate them into 2 lots, that's why I wanted to split the calls to al_save_bitmap into two execution threads. I need to call a thread to save one bitmap while the other thread goes saving another one simultaneously and when one of them finishes it receives the next bitmap.

The program works perfect, I can process more than 1200 photos of 8 mp a day,
but I need more speed, so I thought about using several processor cores through the creation of threads.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Peter Hull
Member #1,136
March 2001

You might want to look at "Thread Pools" or "Producer-consumer queues". It should be possible to find examples using Posix pthreads which is quite close to Allegro's thread API.

First though, are sure that your processing is not bound by the time taken to write out to disk? If it is, you won't see much benefit from multithreading.

alex glez
Member #16,757
October 2017

Yes, the problem is that it runs in series and not in parallel. I do not know how to do it in parallel. I think the processing time is longer than the time needed to write to the disk. My knowledge is very limited, I thought I could do in a simple way two threads in parallel that contain only the "al_save_bitmap" instruction. Could you give me some basic example of how to do it?

Peter Hull
Member #1,136
March 2001

Have a look at:
https://gist.github.com/pedro-w/2de196b801b44f0bc77f745d0c9ea509

It's a start and hopefully others will suggest a better way to do it.
(should definitely use a condition variable rather than that unlock-sleep-lock thing but I never can remember how to use them properly)

alex glez
Member #16,757
October 2017

Thanks to all, the example of the link seems the way forward. I hope to understand ...

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

I don't think allegro supports saving bitmaps from a thread other than the one whose display is current. I've got a working ThreadPool, and if I save the bitmaps from the spawned threads, they are black. If I save them from main, they look right.

In fact, the GDI save routines fail to acquire a lock on the bitmap to save, so no data is copied to the GDI struct which gets saved.

addons/image/gdiplus.cpp#SelectExpand
371bool _al_save_gdiplus_bitmap_f(ALLEGRO_FILE *fp, const char *ident, 372 ALLEGRO_BITMAP *a_bmp) 373{ 374 CLSID encoder; 375 int encoder_status = -1; 376 377 if (!_al_stricmp(ident, ".bmp")) { 378 encoder_status = GetEncoderClsid(L"image/bmp", &encoder); 379 } 380 else if (!_al_stricmp(ident, ".jpg") || !_al_stricmp(ident, ".jpeg")) { 381 encoder_status = GetEncoderClsid(L"image/jpeg", &encoder); 382 } 383 else if (!_al_stricmp(ident, ".gif")) { 384 encoder_status = GetEncoderClsid(L"image/gif", &encoder); 385 } 386 else if (!_al_stricmp(ident, ".tif") || !_al_stricmp(ident, ".tiff")) { 387 encoder_status = GetEncoderClsid(L"image/tiff", &encoder); 388 } 389 else if (!_al_stricmp(ident, ".png")) { 390 encoder_status = GetEncoderClsid(L"image/png", &encoder); 391 } 392 393 if (encoder_status == -1) { 394 ALLEGRO_ERROR("Invalid encoder status.\n"); 395 return false; 396 } 397 398 AllegroWindowsStream *s = new AllegroWindowsStream(fp); 399 if (!s) { 400 ALLEGRO_ERROR("Couldn't create AllegroWindowsStream.\n"); 401 return false; 402 } 403 404 const int w = al_get_bitmap_width(a_bmp), h = al_get_bitmap_height(a_bmp); 405 bool ret = false; 406 407 Gdiplus::Bitmap *gdi_bmp = new Gdiplus::Bitmap(w, h, PixelFormat32bppARGB); 408 409 if (gdi_bmp) { 410 Gdiplus::Rect rect(0, 0, w, h); 411 Gdiplus::BitmapData *gdi_lock = new Gdiplus::BitmapData(); 412 413 if (!gdi_bmp->LockBits(&rect, Gdiplus::ImageLockModeWrite, 414 PixelFormat32bppARGB, gdi_lock)) { 415 416 ALLEGRO_LOCKED_REGION *a_lock = al_lock_bitmap( 417 a_bmp, ALLEGRO_PIXEL_FORMAT_ARGB_8888, ALLEGRO_LOCK_READONLY); 418
419 if (a_lock) {
420 unsigned char *in = (unsigned char *)a_lock->data; 421 unsigned char *out = (unsigned char *)gdi_lock->Scan0; 422 423 if (gdi_lock->Stride == a_lock->pitch) { 424 memcpy(out, in, h * gdi_lock->Stride); 425 } 426 else { 427 uint32_t rows = h; 428 while (rows--) { 429 memcpy(out, in, w * 4); 430 in += a_lock->pitch; 431 out += gdi_lock->Stride; 432 } 433 } 434 435 al_unlock_bitmap(a_bmp); 436 } 437 gdi_bmp->UnlockBits(gdi_lock); 438 } 439 440 ret = (gdi_bmp->Save(s, &encoder, NULL) == 0); 441 442 delete gdi_lock; 443 delete gdi_bmp; 444 } 445 446 s->Release(); 447 448 return ret; 449}

SiegeLord
Member #7,827
October 2006
avatar

I don't think allegro supports saving bitmaps from a thread other than the one whose display is current. I've got a working ThreadPool, and if I save the bitmaps from the spawned threads, they are black. If I save them from main, they look right.

In fact, the GDI save routines fail to acquire a lock on the bitmap to save, so no data is copied to the GDI struct which gets saved.

Yeah, that's expected, but perhaps not well documented.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Peter Hull
Member #1,136
March 2001

I did not know that. Is it also true for memory bitmaps? I was drawing into memory bitmaps and saving them from 2 threads, and it worked OK on macos.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

On Windows, it works for memory bitmaps. I just tested it.

I've also been running some informal tests with my ThreadPool. 8 jobs versus 1 is faster by a factor of between 2 and 3 times.

This is some example code that uses my ThreadPool class that I've put together over the last few days. I will integrate it into Eagle soon, but this version is standalone (only depends on Allegro 5). API is super simple.

#SelectExpand
1#include "Pool.hpp" 2 3class Data { 4public : 5 int x; 6 Data() : x(0) {static int i = 1;x = i++;} 7}; 8void* Factorialize(ALLEGRO_THREAD* t , void* data) { 9 Data* d = (Data*)data; 10 int z = d->x; 11 while (--z) { 12 d->x *= z; 13 } 14 return data; 15} 16int main(int argc , char** argv) { 17 if (!al_init()) {return -1;} 18 19 const int NDAT = 100; 20 Data* dat = new Data[NDAT]; 21 22 ThreadPool tpool; 23 tpool.SetNumThreads(8); 24 for (int i = 0 ; i < NDAT ; ++i) { 25 tpool.AddJob(Factorialize , &dat[i]); 26 } 27 28 tpool.Start(); 29 tpool.Finish(); 30 31 for (int i = 0 ; i < NDAT ; ++i) { 32 Data* d = &dat[i]; 33 printf("%d factorial is %d.\n" , i + 1 , d->x); 34 } 35 36 return 0; 37}

I'm not going to post it yet, for some reason I can't have more than exactly 853 jobs. ??? If I add 854 jobs or more, the thread pool breaks and never finishes its work. ???

UPDATE :
I fixed avoided the strange thread problem and have a working implementation of a thread pool now. You can see it here :
https://www.allegro.cc/forums/thread/617504

Go to: