Tutorial de SDL – Parte 46 – Multithreading

Continuando nossa série de artigos traduzidos do site lazyfoo, veremos agora como usar multithreading para executar duas ou mais funções ao mesmo tempo.
Multithreading pode ser usada para fazer com que o seu programa execute duas coisas ao mesmo tempo e tomar proveito de arquiteturas multihread. Aqui iremos criar um programa simples que exibe informação no terminal enquanto a thread principal é executada.

Existe um adágio em ciência da computação que diz que: Otimização prematura é a raiz de todo o mal

Um grande problema de progreamadores novatos é que eles querem parecer profissionais sem serem. Eles ouvem sobre uma tecnologia que os desenvolvedores mais experientes estão usando e pensam que podem usar também para torna-los magicamente melhores.

Uma dessas ferramentas é multithreading. Desde que processadores com vários núcleos foram lançados para consumidores no inicio dos anos 2000, desenvolvedores tem usado essa nova tecnologia para obter o máximo de performance de suas aplicações.

Aqui vai um ponto importante: um programa multithread feito de forma mal feita pode ser executado de forma mais ineficiente do que um programa que roda em uma única thread. Muito mais mesmo. O fato é que multithread inerentemente adiciona mais trabalho ao programa pois as threads precisam ser gerenciadas. Se você não souber os custos de usar diferentes ferramentas de multithreading, você acabar com código que é muito mais lente do que seu equivalente que é executado em uma única thread.

A regra geral é que se você não souber:
O que é coerência de cache.
O que é alinhamento de cache.
Como sistemas operacionais lidam com thread e processos.
Como usar um profiler.

Você não deve tentar usar otimização por multithreading. Brinque com fogo e você se queimará. Porém, fazer algo não apenas por performance, como carregamento assincrono de arquivos, pode não ser uma ideia ruim para desenvolvedores intermediários.

//Using SDL, SDL Threads, SDL_image, standard IO, and, strings
#include <SDL.h>
#include <SDL_thread.h>
#include <SDL_image.h>
#include <stdio.h>
#include <string>

Quando quisermos usar threads precisamos nos certificar que incluir os arquivos de cabeçalho do SDL.

//Our test thread function
int threadFunction( void* data );

Assim como no caso de funções de callback que usamos jos artigo sobre timers, funções de thread precisam ser declaradas de uma forma específica. Elas precisam ter um argumento que é um pointeiro void e retornar um inteiro.

int threadFunction( void* data )
{
    //Print incoming data
    printf( "Running thread with value = %d\n", (int)data );
    return 0;
}

Nossa função de thread é bem simples. Tudo que ela faz é ler o argumento como um inteiro e usa-lo para exibir uma mensagem no terminal.

            //Run the thread
            int data = 101;
            SDL_Thread* threadID = SDL_CreateThread( threadFunction, "LazyThread", (void*)data );
            //While application is running
            while( !quit )
            {
                //Handle events on queue
                while( SDL_PollEvent( &e ) != 0 )
                {
                    //User requests quit
                    if( e.type == SDL_QUIT )
                    {
                        quit = true;
                    }
                }
                //Clear screen
                SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
                SDL_RenderClear( gRenderer );
                //Render prompt
                gSplashTexture.render( 0, 0 );
                //Update screen
                SDL_RenderPresent( gRenderer );
            }
            //Remove timer in case the call back was not called
            SDL_WaitThread( threadID, NULL );

Antes de entrarmos no loop principal, executamos a função de thread usando SDL_CreatThread. Essa chamada irá executar a função do primeiro argumento, nomear com o segundo argumento (nomes são usados para identificação na depuração) e passa para a função os dados do terceiro argumento.

A threadentão é executada enquanto a thread principal ainda está sendo executada. Caso o loop prinicipal seja encerrado antes da thread ser encerrada, fazemos uma chamada para SDL_WaitThread para nos certificarmos de que a thread tenha sido encerrada antes da aplicação ser finalizada.

Baixe os arquivo de mídia e do código fonte dessa artigo aqui.