Cocos2d-x Tutorials (36)-Multi-threaded and asynchronous loading

Source: Internet
Author: User
Tags posix

Welcome to join the Cocos2d-x Exchange Group: 193411763


When reproduced, please indicate the original source: http://blog.csdn.net/u012945598/article/details/41312345


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



1. Why Use Multithreading

Cocos2d-x is a single-threaded loop engine that updates the state of elements in the game between each frame to ensure that they do not interfere with each other, although it appears as if the program is running in parallel, but it is actually a serial process. The advantage of a single thread is that we don't have to worry about thread-safety issues with game object updates, but when our program encounters some time-consuming I/O operations, the drawbacks of the single thread are revealed.

For example, when the game is in a scene jump, we usually release the resources of the current scene and load the next scene's resources, so that the next interface needs to load the texture into memory, this process needs to read and write the resource files, and this external storage operation is very time-consuming, If you need to load a large number of pictures and high resolution, it is likely to cause our main thread to block, because the processor can not be in such a short time interval (the default frame rate of 1/60) to complete such a large amount of computation, and the program because only one thread will not interrupt the current execution of content to execute other content, So at this point we will observe that the frame rate on the interface dips and even the interface snaps directly.

To circumvent this problem, Cocos2d-x provides the developer with the ability to load asynchronously in the engine, and we can send a request to Texturecache to load the file asynchronously, and Texturecache internally helps us create a new thread to complete the time-consuming loading of the texture operation , and in our main thread we can continue to perform other calculations.

In addition to resource loading, network read and write is also a common time-consuming operation, so using threads in client/server systems is also a common phenomenon, such as asynchronous functionality in HttpClient.


2. Single-core and multi-core

A single core is just one processor, and multicore is multiple processors. Our mobile devices are generally dual or quad-core, such as Iphone6, Samsung Note4, older devices such as IPhone4 's CPU is only single core. The difference between a single-core multi-threaded and multi-core multithreading is illustrated here.


Multithreading in a single-core device is concurrent.

Multithreading in multicore devices is parallel or concurrent.


To explain the meaning of these two words, single core double threading is a very common practice, for example, we write a piece of code with multiple threads, let it run on iphone4, because iphone4 has only one processor, so actually we create a new thread and the main thread, is a cross-running state, for example, we divide the time slice into 100 milliseconds, then the current 100 milliseconds the program executes the main thread, the next 100 milliseconds the program may go to the other threads, and then 100 milliseconds back to the main thread, the advantage is that, Does not leave a thread without a deadline delay, once the time slice into the program will forcibly interrupt the current thread to execute another thread. It looks like they're executing at the same time, but in fact, they're still executed separately.

However, if this code is put on the Samsung Note4 to execute, NOTE4 has a 4-core CPU, on this multiprocessor device, our two threads can occupy one processor per thread, and execute independently, that is, running at the same time without having to run interleaved. In this state, we call it the parallel state. So, concurrency is actually a pseudo-parallel state, which is just a state that pretends to perform multiple operations at the same time.


3. Thread safety issues

First, let's look at a concept, thread safety.

Thread safety means that code can be called by multiple threads without catastrophic results. Here we give a simple example to illustrate (here I use the POSIX thread of the thread function format, understand the approximate meaning of the good)


static int Count  = 0;//count is a static global variable    //a method thread 1 thread function    void * A (void * data) {        while (1) {            count + = 1;            printf ("%d\n", count);        }            }    The thread function of the B method thread 2 is    void * B (void * data) {        while (1) {            count + = 1;            printf ("%d\n", count);        }    }

as shown in the code above, let's say that we are now starting two threads, and the thread functions of two threads are set to A and B respectively (here, in order to facilitate understanding of the thread function is written separately, actually write a thread function to let two threads to execute is enough), after running the program we expect the console output is, 123456789 .... (This code may not have any meaning for implementing the functionality ....) Here are just a few examples)

but actually the result of the operation may not be the case, we expect the situation is in each thread function, the count value to do once plus one operation, and then output this count, but because the execution order of different threads is unpredictable, the above code is likely to occur (assuming the device is a single core) : The initial value of Count is 0, now it is run to thread 1, a is executed to count +=1, when the count value is equal to 1, should output 1, but just the time slice ends here, and now it is switched to thread 2, when the value of count in thread 2 is already 1, Now do it again add 1, become 2, then execute the PRINT statement, output a 2, and then the time slice is finished, and then back to the previous output statement, but since the count value has been changed again by thread 2, then we may see the output on the screen is   223456789 ..... 。

Of course, I said this situation may not necessarily occur, because the above mentioned, the execution order of different threads is unpredictable, each execution will produce different results, perhaps in most cases the output is normal. This example just tells you that the thread is not safe in this situation.


So how to solve the above problem?

First, the count variable for two threads says that it is a shared data, then the two thread that accesses the shared data at the same time can have problems, such as the previous code, thread 1 want to output the count value is 1, However, because thread 2 threads 1 has not output count when the value is changed, but thread 1 does not know that the count value is changed and then continue to execute the output, the result of thread 1 output value is 2.

To solve this problem, the most common method is to make the thread "synchronous", note that the synchronization here is not to let the thread unison run together, we call the thread synchronization, refers to the line threads order to run, you run out, I run again.

The most common way to use thread synchronization is to make memory access to the same data "mutually exclusive". Using our example above to explain that, when the thread 1 in the addition of the count and output operations, thread 2 will not allow access to count, when the Count2 can only be in a blocking state, and so on thread 1 in the face of the count of operations completed, thread 2 can be accessed, Only one thread is allowed to write data at a time, and other threads can only wait.

For example, suppose that I and you each represent a thread, and now I want to perform an operation, the toilet, I go into the toilet, in order to prevent you want to occupy the test, I will lock the toilet, if you want to go to the toilet at this time, then only at the door waiting for me after the unlock left after the use of the toilet. The lock here is like what we call a mutex (mutex). We can ensure that only one thread is able to manipulate the data within a certain time period by locking and unlocking the mutex.

The mutex type in pthread is represented by pthread_mutex_t, and Std::mutex can be used in C + + 11.

For example, just the code can be written as:


    static int Count  = 0;//count is a static global variable/    * Mutex that protects the count operation, */    pthread_mutex_t Count_mutex = Pthread_ Mutex_initializer;        The thread function of a method thread 1    void * A (void * data) {while        (1) {/            * locks the mutex that protects the count operation. *            /Pthread_mutex_lock (&count_mutex);            Count + = 1;            printf ("%d\n", count);            /* The handling of the count operation has been completed, so the lock on the mutex is lifted. *            /Pthread_mutex_nlock (&count_mutex);        }    }


In addition to mutexes, the Sync tool also has semaphores and condition variables, because mutexes can sometimes be a waste of time, even though they meet our needs, and using these tools can help us achieve more complex control patterns.

Considerations for using Multithreading in 4.cocos2d-x

The memory management mechanism used by cocos2d-x and the interface functions of OpenGL are not thread-safe, so do not attempt to invoke the method of memory management provided by the engine within a thread other than the main threads, such as creating an element such as a sprite or layer in a new thread. These elements are called autorelease,autorelease in the Create () method , and retain, release are not thread-safe,and thecontext of OpenGL is not thread-safe. So don't use OpenGL's drawing function in a new thread.


5.pthread Multithreading

Pthread is a multiline libraries, the full name is POSIX thread, because its API adheres to the international formal standard POSIX. Pthread line Libraries is developed by the C language and can be run on multiple platforms, including Andoird,ios, as well as Windows,all the thread functions and data types in Pthread are declared in the <pthread.h> header file, and are Cocos2d-x the recommended multi-threaded libraries prior to use. Now that the 3.x introduces the C++11 feature, the reference to the Pthread library is canceled, and we can use the standard library thread for multithreaded programming.


6. Asynchronous loading

The author's development environment is the Xcode+cocos2d-x 3.3BETA0 version, let's take a brief look at the asynchronous loading process.

we can use a loading interface to achieve a good resource preload, only the resources are loaded into memory, we use such as Sprite,imageview to create objects, it will not cause the phenomenon of lag. So how to load the picture into memory asynchronously, Cocos2d-x provides us with the Addimageasync () method, which is located in the Texturecache class. Let's take a look at the work done in this method

/* Asynchronously adds a texture parameter to the picture's resource path and a callback function that notifies after loading is complete */void texturecache::addimageasync (const std::string &path, const std::        Function<void (texture2d*) >& callback) {//Create a Texture object pointer texture2d *texture = nullptr;        Gets the resource path std::string fullpath = Fileutils::getinstance ()->fullpathforfilename (path);    Returns auto it = _textures.find (fullpath) If the texture is already loaded;        if (it = _textures.end ()) texture = It->second;//second is key-value in value if (texture! = nullptr) {        The texture has been loaded directly to execute the callback method and terminates the function callback (texture);    Return         }//The first time the asynchronous load function is executed, the queue that holds the message structure is initialized if (_asyncstructqueue = = nullptr) {//Two queue release is done in Addimageasynccallback        _asyncstructqueue = new queue<asyncstruct*> ();                _imageinfoqueue = new deque<imageinfo*> ();                Create a new line loads load texture _loadingthread = new Std::thread (&texturecache::loadimage, this);    Whether to exit the variable _needquit = false;   } if (0 = = _asyncrefcount) {/* Register an update callback function with scheduler Cocos2d-x will examine the texture that has already been loaded in this update function and then process a texture for each frame Cache the information for this texture in Texutrecache */director::getinstance ()->getscheduler ()->schedule (Schedule_selec    Tor (Texturecache::addimageasynccallback), this, 0, false);    }//load the number of texture data asynchronously ++_asyncrefcount;    Generates a message structure that loads texture information asynchronously asyncstruct *data = new (Std::nothrow) asyncstruct (FullPath, callback);    Add the generated struct to the queue _asyncstructqueuemutex.lock ();    _asyncstructqueue->push (data);    _asyncstructqueuemutex.unlock (); Unblocking a thread indicates an empty location _sleepcondition.notify_one ();}

In the segment code, it involves a addimageasynccallback method, which is used to check the texture after the asynchronous load is completed, and the first call to Addimageasync will open

void Texturecache::addimageasynccallback (float dt) {//_imageinfoqueue double-ended queue to hold the texture that is loaded in the new thread std::d eque<imageinfo*    > *imagesqueue = _imageinfoqueue; _imageinfomutex.lock (); Lock Mutex if (Imagesqueue->empty ()) {_imageinfomutex.unlock ();//queue is empty to unlock} else {Imageinfo *imageinfo = Imagesqueue->front (); Remove the header element image information Structure body imagesqueue->pop_front ();//Remove the header element _imageinfomutex.unlock ();//Unlock the lock asyncstruct *asyncstruct = imageinfo->asyncstruct;//Gets the asynchronously loaded message structure of the body Image *image = imageinfo->image;//Gets the image pointer used to generate the OpenGL texture  Map const std::string& filename = asyncstruct->filename;//Gets the resource file name//create texture pointer texture2d *texture =        Nullptr                        The image pointer is not empty if (image) {//create texture object texture = new (Std::nothrow) texture2d ();            Generates an OpenGL map Texture->initwithimage (image) from the image pointer; #if cc_enable_cache_texture_data Cache the texture fileName Volatiletexturemgr::addimagetexture (texture, filename); #endif//cache texture Data _textures.i            Nsert (std::make_pair (filename, texture));            Texture->retain ();        Added to the auto-release pool texture->autorelease ();            } else {Auto it = _textures.find (asyncstruct->filename);        if (it = _textures.end ()) texture = it->second; }//Get the function to be notified after loading is complete and notify if (Asyncstruct->callback) {asyncstruct->callback (texture)        ;        }//Releases the image if (image) {image->release ();        }//Release two structure delete asyncstruct;        Delete Imageinfo;        Reduce the number of textures loaded by one--_asyncrefcount; /* All Files loaded complete logout callback function */if (0 = = _asyncrefcount) {director::getinstance ()->getscheduler ()->u        Nschedule (Schedule_selector (Texturecache::addimageasynccallback), this); }    }}

There are a lot of details that are not specifically analyzed, after the successful loading, we only need to call the Addimageasync () method in the callback function specified when the percentage of the progress bar is set to do so.For example:

BOOL Helloworld::init () {    //////////////////////////////    //1. Super init first    if (! Layer::init ())    {        return false;    }        /* Load Texture asynchronously */for    (int i=0; i<10; i++) {        director::getinstance ()->gettexturecache ()->addimageasync (" Helloworld.png ", Cc_callback_1 (Helloworld::imageloadedcallback, this));    }    return true;} void Helloworld::imageloadedcallback (ref* psender) {    //each time a texture is successfully loaded it is possible to set the progress bar in the callback method here. All texture loading is done on the Jump interface}

The asynchronous load demo can be found in texture2dtest.

Cocos2d-x Tutorials (36)-Multi-threaded and asynchronous loading

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.