Looking at multithreading is actually not that terrible-small talk multithreading (1)

Source: Internet
Author: User

Author: Chen Xi

Date: 9:55:28

Environment: [Mac 10.7.1 lion intel I3 supports 64-bit gcc4.2.1 xcode4.2]

Reprinted please indicate the source

Q1: for the main thread, how do I pass parameters to create a subthread?

A: For the pthread thread interface, the thread function parameters meet this requirement. The following code:

#include <stdio.h>#include <pthread.h>#define PRINT_D(intValue)   printf(#intValue" is %d\n", (intValue));#define PRINT_U(uintValue)   printf(#uintValue" is %lu\n", (uintValue));#define PRINT_STR(str)      printf(#str" is %s\n", (str));// son threadvoid    *son_thread_func(void *arg){    char *s = (char *)arg;    PRINT_STR(s)    return NULL;}// main threadint main(int argc, char **argv){    pthread_t son_thread;    int ret;    ret = pthread_create(&son_thread, NULL, son_thread_func, "son thread");      if(ret != 0)    {        perror("pthread_create error");        return -1;    }        ret = pthread_join(son_thread, NULL);    if(ret != 0)    {        perror("pthread_join error");        return -1;    }        printf("[Main Thread]End...\n");        return 0;     }

Running result:

s is son thread[Main Thread]End...

Q2: Can I directly pass the stack variables in the main thread to the subthread using the above passing parameters?

A: Of course. Although the stacks of the main thread and sub-thread are different, the stack belongs to the same process space and can be used by each thread (of course, some are read-only areas ). The following code:

// son threadvoid    *son_thread_func(void *arg){    int *i = (int *)arg;    printf("son thread i: %d\n", *i);    *i = 100;       // modify main thread local var: local    return NULL;}// main threadint main(int argc, char **argv){    pthread_t son_thread;    int ret;    int local = 12;    // pass the local value to son thread    ret = pthread_create(&son_thread, NULL, son_thread_func, &local);      if(ret != 0)    {        perror("pthread_create error");        return -1;    }        ret = pthread_join(son_thread, NULL);    if(ret != 0)    {        perror("pthread_join error");        return -1;    }        // now output the value that be modified by son thread    PRINT_D(local)    printf("[Main Thread]End...\n");        return 0;     }

The above code, header files, and some macro definitions are consistent with the original code, because the space issue will not always use the complete code.

In the above Code, the parent thread passes the local address of the local variable to the sub-thread, and the sub-thread modifies its value. After the sub-thread returns, the parent thread outputs the local value.

son thread i: 12local is 100[Main Thread]End...

Q3: How does a subthread return value void * be used by the main thread?

A: The main thread calls pthread_join to block itself and wait for the sub-thread to complete execution. The second parameter of the pthread_join function can receive the return value of the sub-thread. The following code:

// son threadvoid    *son_thread_func(void *arg){    int *i = (int *)arg;    *i = 100;       // modify main thread local var: local    return (void *)*i;}// main threadint main(int argc, char **argv){    pthread_t son_thread;    int ret;    int local = 12;    void *sonthread_ret;    ret = pthread_create(&son_thread, NULL, son_thread_func, &local);      if(ret != 0)    {        perror("pthread_create error");        return -1;    }        // sonthread_ret will store the son thread's return value    ret = pthread_join(son_thread, &sonthread_ret);      if(ret != 0)    {        perror("pthread_join error");        return -1;    }        PRINT_D((int)sonthread_ret)    printf("[Main Thread]End...\n");        return 0;     }

In the above Code, the second parameter of pthread_join will save the value returned by the sub-thread; the main thread will finally output it. The result is as follows:

(int)sonthread_ret is 100[Main Thread]End...

Q4: If the return values of the pthread_create and pthread_join functions are not 0, it indicates that the statement is unsuccessful and the statement length is a little long. Can it be shortened?

A: Use macros. As follows:

#define PTHREAD_ERROR(func, ret, return_value)     \if((ret) != 0)        \{   \perror(#func" error");  \printf("ret is %d\n", (ret));   \return (return_value);  \}

The above main function code is shortened:

// main threadint main(int argc, char **argv){    pthread_t son_thread;    int ret;    int local = 12;    void *sonthread_ret;    ret = pthread_create(&son_thread, NULL, son_thread_func, &local);      PTHREAD_ERROR(pthread_create, ret, -1)        // sonthread_ret will store the son thread's return value    ret = pthread_join(son_thread, &sonthread_ret);      PTHREAD_ERROR(pthread_join, ret, -1)        PRINT_D((int)sonthread_ret)    printf("[Main Thread]End...\n");        return 0;     }

It seems that the key and the subject are easier to see.

Q5: after the final return value of the main function is changed to return-1, why does the returned value get 255 after it is executed in Bash?


A: This is because in bash, $? It is an unsigned integer and is 1 byte. -1 in the memory, the value of 1 byte is 0xff, so 255 is obtained.


Q6: will the process end when the sub-thread calls exit?

A: Yes. However, pay attention to whether the Sub-thread directly ends the main thread of the process and whether to do anything else. At the same time, the release of memory and resources needs to be correctly handled.

#include <stdio.h>#include <stdlib.h>#include <pthread.h>#define PRINT_D(intValue)   printf(#intValue" is %d\n", (intValue));#define PRINT_U(uintValue)   printf(#uintValue" is %lu\n", (uintValue));#define PRINT_STR(str)      printf(#str" is %s\n", (str));#define RETURN_ERROR(func, ret, return_value)     \if((ret) != 0)        \{   \perror(#func" error");  \printf("ret is %d\n", (ret));   \return (return_value);  \}// son threadvoid    *son_thread_func(void *arg){    exit(-1);    return NULL;}void    exit_process(){    printf("process will exit\n");}// main threadint main(int argc, char **argv){    pthread_t son_thread;    int ret;        ret = atexit(exit_process);    RETURN_ERROR(atexit, ret, -1)        ret = pthread_create(&son_thread, NULL, son_thread_func, NULL);      RETURN_ERROR(pthread_create, ret, -1)        ret = pthread_join(son_thread, NULL);      RETURN_ERROR(pthread_join, ret, -1)        printf("[Main Thread]End...\n");        return 0;     }

In the above Code, the main thread registers the process end event; In the child thread, it calls the Exit End Process, and the final output:

process will exit

As you can see, the function registered by atexit is executed, but the final output of the main thread is not complete. As you can see, the sub-thread calling exit may cause the main thread to fail to complete the operation normally. Therefore, you must be careful when calling exit. At the same time, the previous pthread_error macro is also changed to return_error.

Q7: After pthread_create creates a sub-thread, the sub-thread may have been executed. Is there any way for the sub-thread function code to be executed after this function is returned?

A: You can use mutex. When the main thread calls pthread_create and starts execution with the subthread, it locks with the mutex.

#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <unistd.h>#define PRINT_D(intValue)   printf(#intValue" is %d\n", (intValue));#define PRINT_U(uintValue)   printf(#uintValue" is %lu\n", (uintValue));#define PRINT_STR(str)      printf(#str" is %s\n", (str));#define FOREVER_PRINT       { while(1)    printf("...");}#define RETURN_ERROR(func, ret, return_value)     \if((ret) != 0)        \{   \perror(#func" error");  \printf("ret is %d\n", (ret));   \return (return_value);  \}pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;// son threadvoid    *son_thread_func(void *arg){    int i = 0;        pthread_mutex_lock(&mutex);    printf("son thread:%d\n", i);    pthread_mutex_unlock(&mutex);    return NULL;}// main threadint main(int argc, char **argv){    pthread_t son_thread;    int ret;        pthread_mutex_lock(&mutex);    printf("pthread_create begin...\n");    ret = pthread_create(&son_thread, NULL, son_thread_func, NULL);      RETURN_ERROR(pthread_create, ret, -1)    sleep(1);       printf("pthread_create ok...\n");    pthread_mutex_unlock(&mutex);        ret = pthread_join(son_thread, NULL);    RETURN_ERROR(pthread_join, ret, -1)        printf("[Main Thread]End...\n");        return 0;     }

In the above Code, the main thread used mutex to lock the subthread before creating the subthread code. After the creation, the main thread slept for 1 second and then unlocked. The output result is as follows:

pthread_create begin...pthread_create ok...son thread:0[Main Thread]End...

It can be seen that, although the main thread of the sub-thread is sleeping for 1 second after being created, the sub-thread Function Code cannot be actually executed due to mutex being locked.

Q8: Why can't I see any data output after executing printf?

A: This is probably because printf uses a buffer. If you need to view the output in the console in time, use fflush (stdout) or use a linefeed to end the output.

Q9: how to determine whether the current execution thread is the main thread?

A: If you only execute a function in the main thread or sub-thread, you do not need to judge whether the function is the main thread. However, if both of them call another function, when calling this function, you may need to determine whether the main thread or sub-thread is used.

pthread_t main_thread;void    print_hello(){    if(pthread_equal(pthread_self(), main_thread))        printf("main thread call it\n");    else         printf("son thread call it\n");    printf("hello\n");}// son threadvoid    *son_thread_func(void *arg){    print_hello();    return NULL;}// main threadint main(int argc, char **argv){    pthread_t son_thread;    int ret;        main_thread = pthread_self();   // save main thread    ret = pthread_create(&son_thread, NULL, son_thread_func, NULL);      RETURN_ERROR(pthread_create, ret, -1)        print_hello();        ret = pthread_join(son_thread, NULL);    RETURN_ERROR(pthread_join, ret, -1)        printf("[Main Thread]End...\n");        return 0;     }

Pthread_self obtains the pthread_t data of the current thread. pthread_equal can be used to determine whether the pthread_t type of the thread is the same, so that the thread can be compared to the same.

Output result:

main thread call itson thread call ithellohello[Main Thread]End...

Q10: Why is the output of printf separated when the main thread calls the print_hello function?

A: This reflects the features of concurrent thread execution. When a subthread is created, it starts to run concurrently with the main thread. It can be said that, in general, I don't know which thread will be executed at all. This depends entirely on the scheduling policy of the operating system. Of course, if I intentionally perform sleep and delay, the execution sequence will change. If you do not want the preceding separation, you can change the code:

// main threadint main(int argc, char **argv){    pthread_t son_thread;    int ret;        main_thread = pthread_self();   // save main thread    print_hello();        ret = pthread_create(&son_thread, NULL, son_thread_func, NULL);      RETURN_ERROR(pthread_create, ret, -1)        ret = pthread_join(son_thread, NULL);    RETURN_ERROR(pthread_join, ret, -1)        printf("[Main Thread]End...\n");        return 0;     }

Put the print_hello of the main thread in front of the child thread to avoid output confusion:

main thread call ithelloson thread call ithello[Main Thread]End...

Q11: why are the output results of the two threads calling printf respectively not displayed in disorder? Does printf implement mutual access?

A: Yes. POSIX standards require that ansi I/O functions implement mutually exclusive access. I/O functions in C language are basically mutually exclusive access on mainstream platforms. Of course, each platform may also retain I/O functions that do not implement mutex access. For example, putchar_unlocked function. Putchar_unlocked corresponds to putchar. It indicates that the output is not locked and the latter is locked. The following compares their efficiency:

#include <time.h>#defineMACRO_TIME_BEGIN(loopCount)\{\clock_t begin, end;\begin = clock();\for(int i = 0; i < (loopCount); ++i)\{\#defineMACRO_TIME_END\}\end = clock();\printf("\ntime is %f s\n", (double)(end - begin) / CLOCKS_PER_SEC);\}flockfile(stdout);              // lock stdoutMACRO_TIME_BEGIN(100000)putchar_unlocked('a'); MACRO_TIME_END                  // 0.170801 sfunlockfile(stdout);            // unlock stdout    MACRO_TIME_BEGIN(100000)putchar('a');MACRO_TIME_END                  // 0.193599 s

Both operations are performed 0.1 million times. the time consumed above indicates an average value of several tests. It can be seen that putchar_unlocked is less time-consuming than the process of adding and unlocking.


Q12: Can I modify the stack size of a newly created thread?

A: For the main thread, you can set the stack size according to the system or compiler. For the sub-thread, you can set the stack size through pthread_attr_setstacksize, but you must input this attribute parameter when creating the thread. The following is an example of obtaining the stack size:

// son threadvoid    *son_thread_func(void *arg){    // use a 512KB stack, buf is too big that it will crash    char buf[524288] = {0};         return NULL;}// main threadint main(int argc, char **argv){    pthread_t son_thread;    int ret;    pthread_attr_t thread_attr;    size_t stack_size;        ret = pthread_attr_init(&thread_attr);    RETURN_ERROR(pthread_attr_init, ret, -1)    ret = pthread_attr_getstacksize(&thread_attr, &stack_size);    RETURN_ERROR(pthread_attr_getstacksize, ret, -1)    PRINT_U(stack_size)        ret = pthread_create(&son_thread, &thread_attr, son_thread_func, NULL);      RETURN_ERROR(pthread_create, ret, -1)        ret = pthread_attr_destroy(&thread_attr);    RETURN_ERROR(pthread_attr_destroy, ret, -1)    ret = pthread_join(son_thread, NULL);    RETURN_ERROR(pthread_join, ret, -1)        printf("[Main Thread]End...\n");        return 0;     }

The second parameter of pthread_create is passed into the pthread_attr_t type attribute parameter, which contains the stack size. Run:

stack_size is 524288Bus error: 10

We can see that the stack size of the sub-thread is 524288 by default, that is, kb. An error occurs in the subsequent execution. Use xcode for debugging,

It can be seen that an exception occurs in the Buf of the sub-thread stack object. For the stack size, the default master thread of the MAC system is 8 Mb, the default sub-thread is kb, the default master thread on iOS is 1 MB, and the sub-thread is kb. the following code modifies the sub-thread stack so that the sub-thread does not crash:

// main threadint main(int argc, char **argv){    pthread_t son_thread;    int ret;    pthread_attr_t thread_attr;    size_t stack_size;        ret = pthread_attr_init(&thread_attr);    RETURN_ERROR(pthread_attr_init, ret, -1)    ret = pthread_attr_getstacksize(&thread_attr, &stack_size);    RETURN_ERROR(pthread_attr_getstacksize, ret, -1)    PRINT_U(stack_size)        // set big stack, than the son thread won't crash    ret = pthread_attr_setstacksize(&thread_attr, stack_size * 2);        RETURN_ERROR(pthread_attr_setstacksize, ret, -1)        ret = pthread_attr_getstacksize(&thread_attr, &stack_size);    RETURN_ERROR(pthread_attr_getstacksize, ret, -1)    printf("stack_size new value: %d\n", stack_size);        ret = pthread_create(&son_thread, &thread_attr, son_thread_func, NULL);      RETURN_ERROR(pthread_create, ret, -1)        ret = pthread_attr_destroy(&thread_attr);    RETURN_ERROR(pthread_attr_destroy, ret, -1)    ret = pthread_join(son_thread, NULL);    RETURN_ERROR(pthread_join, ret, -1)        printf("[Main Thread]End...\n");        return 0;     }

Output result:

stack_size is 524288stack_size new value: 1048576[Main Thread]End...

The program ends normally.

This article mainly describes the basic process of multi-thread creation. The next article will focus on the precautions for multi-thread exit.

Author: Chen Xi

Date: 9:55:28

Environment: [Mac 10.7.1 lion intel I3 supports 64-bit gcc4.2.1 xcode4.2]

Reprinted please indicate the source

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.