C++ truyền nhiều đối số cho hàm

/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
4 áp đặt một định dạng nghiêm ngặt đối với nguyên mẫu của hàm sẽ chạy trong chuỗi mới. Nó phải nhận một tham số
/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
5 duy nhất và trả về một giá trị
/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
5 duy nhất. Tham số cuối cùng của
/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
4 được truyền dưới dạng đối số cho hàm, trong khi giá trị trả về được truyền bằng cách sử dụng
/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
8 và
/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
9. Phần này xem xét chi tiết của các cơ chế này và tác động của chúng

6. 5. 1. Truyền một đối số duy nhất cho chủ đề

Việc truyền một đối số cho một chuỗi có vẻ đơn giản, nhưng rất dễ thực hiện sai. Là một ví dụ đơn giản để minh họa sự nguy hiểm, được thiết kế để chạy trong một luồng riêng biệt

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15

/* Code Listing 6.5:
   A thread that will print a single integer value
 */

void *
child_thread (void *args)
{
  /* POTENTIALLY DANGEROUS TIMING */
  int *argptr = (int *) args;
  int arg = *argptr;

  /* Print the local copy of the argument */
  printf ("Argument is %d\n", arg);
  pthread_exit (NULL);
}

Sự nguy hiểm của mã này có thể được minh họa bằng vòng lặp trong. Mục đích là chuyển giá trị 1 cho chuỗi đầu tiên, 2 cho chuỗi thứ hai, v.v. Tuy nhiên, điều quan trọng cần lưu ý là chỉ có một bản sao duy nhất của biến

/* Code Listing 6.7:
   Each thread should be given a separate value, rather than a shared address
 */

/* FIXED VERSION */
/* ints are passed by value, so a COPY gets passed to each call */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, (void *)i) == 0);
0. Nghĩa là, mã này chuyển địa chỉ của một biến cho tất cả 10 luồng;

/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);

Vấn đề mấu chốt là việc tạo và thực thi luồng là. Điều đó có nghĩa là không thể dự đoán khi nào mỗi luồng mới bắt đầu chạy. Một thời điểm có thể xảy ra là tất cả 10 luồng được tạo trước, dẫn đến việc

/* Code Listing 6.7:
   Each thread should be given a separate value, rather than a shared address
 */

/* FIXED VERSION */
/* ints are passed by value, so a COPY gets passed to each call */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, (void *)i) == 0);
0 lưu trữ giá trị 11. Tại thời điểm đó, mỗi chủ đề hủy đăng ký biến
/* Code Listing 6.7:
   Each thread should be given a separate value, rather than a shared address
 */

/* FIXED VERSION */
/* ints are passed by value, so a COPY gets passed to each call */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, (void *)i) == 0);
2 tương ứng của chúng và tất cả đều nhận được cùng một giá trị là 11

Một giải pháp phổ biến cho vấn đề này là truyền các giá trị số dưới dạng con trỏ, như minh họa trong. Nghĩa là, biến int

/* Code Listing 6.7:
   Each thread should be given a separate value, rather than a shared address
 */

/* FIXED VERSION */
/* ints are passed by value, so a COPY gets passed to each call */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, (void *)i) == 0);
0 được sử dụng làm đối số
/* Code Listing 6.7:
   Each thread should be given a separate value, rather than a shared address
 */

/* FIXED VERSION */
/* ints are passed by value, so a COPY gets passed to each call */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, (void *)i) == 0);
4 trong lệnh gọi tới
/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
4. Sau đó, đối số
/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
5 thành
/* Code Listing 6.7:
   Each thread should be given a separate value, rather than a shared address
 */

/* FIXED VERSION */
/* ints are passed by value, so a COPY gets passed to each call */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, (void *)i) == 0);
7 chuyển đối số trở lại đối số
/* Code Listing 6.7:
   Each thread should be given a separate value, rather than a shared address
 */

/* FIXED VERSION */
/* ints are passed by value, so a COPY gets passed to each call */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, (void *)i) == 0);
8

________số 8_______

Điều làm cho mã này hoạt động là thực tế là các biến vô hướng (e. g. ,

/* Code Listing 6.7:
   Each thread should be given a separate value, rather than a shared address
 */

/* FIXED VERSION */
/* ints are passed by value, so a COPY gets passed to each call */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, (void *)i) == 0);
8 biến) được truyền bằng cách sử dụng ngữ nghĩa gọi theo giá trị. Khi mã này chuẩn bị cho lệnh gọi
/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
4, một bản sao riêng của giá trị hiện tại của biến
/* Code Listing 6.7:
   Each thread should be given a separate value, rather than a shared address
 */

/* FIXED VERSION */
/* ints are passed by value, so a COPY gets passed to each call */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, (void *)i) == 0);
0 được đặt vào một thanh ghi hoặc vào ngăn xếp. hiển thị phiên bản đã sửa của. Hàm
/* Code Listing 6.7:
   Each thread should be given a separate value, rather than a shared address
 */

/* FIXED VERSION */
/* ints are passed by value, so a COPY gets passed to each call */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, (void *)i) == 0);
7 sau đó nhận bản sao này, bất kể có bất kỳ thay đổi nào đối với biến
/* Code Listing 6.7:
   Each thread should be given a separate value, rather than a shared address
 */

/* FIXED VERSION */
/* ints are passed by value, so a COPY gets passed to each call */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, (void *)i) == 0);
0 ban đầu. Sau đó, khi luồng con truyền tham số
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
4 của nó sang một miền cục bộ
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
5, nó đang hoạt động với giá trị chính xác đã được truyền

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21

/* Code Listing 6.8:
   A safer version of Code Listing 6.5
 */

/* Convention: It is common to name a void* parameter with a name
   that begins with _, then cast it to a local variable that has
   the same (or nearly the same) name without the _. So _args will
   become args. Recall that _ has no special meaning and is treated
   like a normal alphabetical character. */

void *
child_thread (void *_args)
{
  /* Safe whenever size of int <= size of pointer (which is
     usually true) */
  int arg = (int) _args;

  /* Print the local copy of the argument */
  printf ("Argument is %ld\n", arg);
  pthread_exit (NULL);
}

Cảnh báo lỗi


Truyền các giá trị tích phân cho con trỏ và ngược lại là một cách phổ biến để truyền tham số cho pthread. Tuy nhiên, trong khi nó nói chung là an toàn trong thực tế, nó có thể là một lỗi trên một số nền tảng. Cụ thể, kỹ thuật này dựa trên thực tế là các con trỏ có kích thước ít nhất bằng các kiểu số nguyên tiêu chuẩn. Nghĩa là, các biến

/* Code Listing 6.7:
   Each thread should be given a separate value, rather than a shared address
 */

/* FIXED VERSION */
/* ints are passed by value, so a COPY gets passed to each call */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, (void *)i) == 0);
8 thường (nhưng không bắt buộc) có kích thước 32 bit. Kiến trúc CPU hiện đại có xu hướng sử dụng địa chỉ 32 hoặc 64 bit. Như vậy, truyền một 32-bit
/* Code Listing 6.7:
   Each thread should be given a separate value, rather than a shared address
 */

/* FIXED VERSION */
/* ints are passed by value, so a COPY gets passed to each call */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, (void *)i) == 0);
8 lên một _______ 0_______5 rồi quay lại 32-bit 32-bit
/* Code Listing 6.7:
   Each thread should be given a separate value, rather than a shared address
 */

/* FIXED VERSION */
/* ints are passed by value, so a COPY gets passed to each call */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, (void *)i) == 0);
8 là an toàn

Mặt khác, giả sử đối số được khai báo là một thể hiện của biến

/* Code Listing 6.8:
   A safer version of Code Listing 6.5
 */

/* Convention: It is common to name a void* parameter with a name
   that begins with _, then cast it to a local variable that has
   the same (or nearly the same) name without the _. So _args will
   become args. Recall that _ has no special meaning and is treated
   like a normal alphabetical character. */

void *
child_thread (void *_args)
{
  /* Safe whenever size of int <= size of pointer (which is
     usually true) */
  int arg = (int) _args;

  /* Print the local copy of the argument */
  printf ("Argument is %ld\n", arg);
  pthread_exit (NULL);
}
0. Nếu mã đang chạy trên kiến ​​trúc 32 bit (điều này không phổ biến đối với các hệ thống ảo hóa) nhưng loại
/* Code Listing 6.8:
   A safer version of Code Listing 6.5
 */

/* Convention: It is common to name a void* parameter with a name
   that begins with _, then cast it to a local variable that has
   the same (or nearly the same) name without the _. So _args will
   become args. Recall that _ has no special meaning and is treated
   like a normal alphabetical character. */

void *
child_thread (void *_args)
{
  /* Safe whenever size of int <= size of pointer (which is
     usually true) */
  int arg = (int) _args;

  /* Print the local copy of the argument */
  printf ("Argument is %ld\n", arg);
  pthread_exit (NULL);
}
0 có kích thước 64 bit, thì một nửa đối số sẽ bị mất do truyền xuống con trỏ cho lệnh gọi tới
/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
4

6. 5. 2. Truyền nhiều đối số cho chủ đề

Khi truyền nhiều đối số cho một luồng con, cách tiếp cận tiêu chuẩn là nhóm các đối số trong một khai báo

/* Code Listing 6.8:
   A safer version of Code Listing 6.5
 */

/* Convention: It is common to name a void* parameter with a name
   that begins with _, then cast it to a local variable that has
   the same (or nearly the same) name without the _. So _args will
   become args. Recall that _ has no special meaning and is treated
   like a normal alphabetical character. */

void *
child_thread (void *_args)
{
  /* Safe whenever size of int <= size of pointer (which is
     usually true) */
  int arg = (int) _args;

  /* Print the local copy of the argument */
  printf ("Argument is %ld\n", arg);
  pthread_exit (NULL);
}
3, như minh họa trong. Địa chỉ của phiên bản
/* Code Listing 6.8:
   A safer version of Code Listing 6.5
 */

/* Convention: It is common to name a void* parameter with a name
   that begins with _, then cast it to a local variable that has
   the same (or nearly the same) name without the _. So _args will
   become args. Recall that _ has no special meaning and is treated
   like a normal alphabetical character. */

void *
child_thread (void *_args)
{
  /* Safe whenever size of int <= size of pointer (which is
     usually true) */
  int arg = (int) _args;

  /* Print the local copy of the argument */
  printf ("Argument is %ld\n", arg);
  pthread_exit (NULL);
}
3 được chuyển thành
/* Code Listing 6.8:
   A safer version of Code Listing 6.5
 */

/* Convention: It is common to name a void* parameter with a name
   that begins with _, then cast it to a local variable that has
   the same (or nearly the same) name without the _. So _args will
   become args. Recall that _ has no special meaning and is treated
   like a normal alphabetical character. */

void *
child_thread (void *_args)
{
  /* Safe whenever size of int <= size of pointer (which is
     usually true) */
  int arg = (int) _args;

  /* Print the local copy of the argument */
  printf ("Argument is %ld\n", arg);
  pthread_exit (NULL);
}
5 đến
/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
4. Điểm vào của chủ đề mới nhận được tham số
/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
5 mà sau đó có thể được chuyển thành loại
/* Code Listing 6.8:
   A safer version of Code Listing 6.5
 */

/* Convention: It is common to name a void* parameter with a name
   that begins with _, then cast it to a local variable that has
   the same (or nearly the same) name without the _. So _args will
   become args. Recall that _ has no special meaning and is treated
   like a normal alphabetical character. */

void *
child_thread (void *_args)
{
  /* Safe whenever size of int <= size of pointer (which is
     usually true) */
  int arg = (int) _args;

  /* Print the local copy of the argument */
  printf ("Argument is %ld\n", arg);
  pthread_exit (NULL);
}
3

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17

/* Code Listing 6.9:
   Passing multiple arguments to a thread requires grouping them into a struct
 */

/* Assume we have:
     struct thread_args {
       int first;
       const char *second;
     };
 */

struct thread_args *args = malloc (sizeof (struct thread_args));
args->first = 5;
args->second = "Hello";

/* Note that the data structure resides on the heap */
assert (pthread_create (&child, NULL, hello_thread, args) == 0);

hiển thị luồng mới đang nhận con trỏ tới

/* Code Listing 6.8:
   A safer version of Code Listing 6.5
 */

/* Convention: It is common to name a void* parameter with a name
   that begins with _, then cast it to a local variable that has
   the same (or nearly the same) name without the _. So _args will
   become args. Recall that _ has no special meaning and is treated
   like a normal alphabetical character. */

void *
child_thread (void *_args)
{
  /* Safe whenever size of int <= size of pointer (which is
     usually true) */
  int arg = (int) _args;

  /* Print the local copy of the argument */
  printf ("Argument is %ld\n", arg);
  pthread_exit (NULL);
}
3 và giải phóng bộ nhớ được cấp phát khi xử lý xong dữ liệu

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16

/* Code Listing 6.10:
   The child thread receives multiple values through the passed struct
 */

/* Using the convention of casting _args to args */
void *
hello_thread (void *_args)
{
  /* Cast args into a meaningful pointer type that we can use */
  struct thread_args *args = (struct thread_args *) _args;
  printf ("First: %d; Second: '%s'\n", args->first, args->second);

  /* Do not forget to free the struct used for arguments */
  free (args);
  pthread_exit (NULL);
}

Cảnh báo lỗi


Một lỗi phổ biến khi truyền đối số theo cách này là khai báo đối tượng

/* Code Listing 6.8:
   A safer version of Code Listing 6.5
 */

/* Convention: It is common to name a void* parameter with a name
   that begins with _, then cast it to a local variable that has
   the same (or nearly the same) name without the _. So _args will
   become args. Recall that _ has no special meaning and is treated
   like a normal alphabetical character. */

void *
child_thread (void *_args)
{
  /* Safe whenever size of int <= size of pointer (which is
     usually true) */
  int arg = (int) _args;

  /* Print the local copy of the argument */
  printf ("Argument is %ld\n", arg);
  pthread_exit (NULL);
}
3 dưới dạng biến cục bộ thay vì sử dụng phân bổ động. Một lần nữa, vấn đề là bản chất không đồng bộ của
/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
4. Hãy xem xét mã mẫu này

/* Code Listing 6.5:
   A thread that will print a single integer value
 */

void *
child_thread (void *args)
{
  /* POTENTIALLY DANGEROUS TIMING */
  int *argptr = (int *) args;
  int arg = *argptr;

  /* Print the local copy of the argument */
  printf ("Argument is %d\n", arg);
  pthread_exit (NULL);
}
0

/* Code Listing 6.5:
   A thread that will print a single integer value
 */

void *
child_thread (void *args)
{
  /* POTENTIALLY DANGEROUS TIMING */
  int *argptr = (int *) args;
  int arg = *argptr;

  /* Print the local copy of the argument */
  printf ("Argument is %d\n", arg);
  pthread_exit (NULL);
}
1

Nếu luồng con chạy ngay trước khi

/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
4 trả về, thì mọi thứ sẽ ổn. Tuy nhiên, không có gì đảm bảo rằng điều này xảy ra. Thay vào đó, rất có thể là
/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
4 trả về và luồng gốc thoát ra. Khi điều đó xảy ra, tất cả dữ liệu trên ngăn xếp của chuỗi gốc (bao gồm cả phiên bản
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
4) trở nên không hợp lệ. Chuỗi con hiện có một con trỏ lơ lửng tới dữ liệu có khả năng bị hỏng. Đây là một ví dụ khác về điều kiện chủng tộc có thể xảy ra với chủ đề

6. 5. 3. Trả lại giá trị từ chủ đề

Có ba cách phổ biến để lấy lại các giá trị trả về từ một luồng. Cả ba kỹ thuật sử dụng tương tự như những kỹ thuật được sử dụng để truyền đối số. chỉ ra một kỹ thuật đơn giản, đó là tăng cường khai báo

/* Code Listing 6.8:
   A safer version of Code Listing 6.5
 */

/* Convention: It is common to name a void* parameter with a name
   that begins with _, then cast it to a local variable that has
   the same (or nearly the same) name without the _. So _args will
   become args. Recall that _ has no special meaning and is treated
   like a normal alphabetical character. */

void *
child_thread (void *_args)
{
  /* Safe whenever size of int <= size of pointer (which is
     usually true) */
  int arg = (int) _args;

  /* Print the local copy of the argument */
  printf ("Argument is %ld\n", arg);
  pthread_exit (NULL);
}
3 để bao gồm khoảng trống cho bất kỳ giá trị trả về nào

/* Code Listing 6.5:
   A thread that will print a single integer value
 */

void *
child_thread (void *args)
{
  /* POTENTIALLY DANGEROUS TIMING */
  int *argptr = (int *) args;
  int arg = *argptr;

  /* Print the local copy of the argument */
  printf ("Argument is %d\n", arg);
  pthread_exit (NULL);
}
2

/* Code Listing 6.5:
   A thread that will print a single integer value
 */

void *
child_thread (void *args)
{
  /* POTENTIALLY DANGEROUS TIMING */
  int *argptr = (int *) args;
  int arg = *argptr;

  /* Print the local copy of the argument */
  printf ("Argument is %d\n", arg);
  pthread_exit (NULL);
}
3

Luồng con nhận một con trỏ tới thể hiện

/* Code Listing 6.8:
   A safer version of Code Listing 6.5
 */

/* Convention: It is common to name a void* parameter with a name
   that begins with _, then cast it to a local variable that has
   the same (or nearly the same) name without the _. So _args will
   become args. Recall that _ has no special meaning and is treated
   like a normal alphabetical character. */

void *
child_thread (void *_args)
{
  /* Safe whenever size of int <= size of pointer (which is
     usually true) */
  int arg = (int) _args;

  /* Print the local copy of the argument */
  printf ("Argument is %ld\n", arg);
  pthread_exit (NULL);
}
3, sử dụng các tham số đầu vào nếu cần. Trong trường hợp này, các giá trị của
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
7 và
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
8 được thêm vào và tổng kết quả được sao chép trở lại vào
/* Code Listing 6.8:
   A safer version of Code Listing 6.5
 */

/* Convention: It is common to name a void* parameter with a name
   that begins with _, then cast it to a local variable that has
   the same (or nearly the same) name without the _. So _args will
   become args. Recall that _ has no special meaning and is treated
   like a normal alphabetical character. */

void *
child_thread (void *_args)
{
  /* Safe whenever size of int <= size of pointer (which is
     usually true) */
  int arg = (int) _args;

  /* Print the local copy of the argument */
  printf ("Argument is %ld\n", arg);
  pthread_exit (NULL);
}
3. Như được hiển thị trong , luồng chính sử dụng
/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
9 để đợi cho đến khi luồng con thoát. Sau khi phần tử con kết thúc, chuỗi chính có thể truy xuất cả ba giá trị (
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
7,
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
8 và
/* Code Listing 6.9:
   Passing multiple arguments to a thread requires grouping them into a struct
 */

/* Assume we have:
     struct thread_args {
       int first;
       const char *second;
     };
 */

struct thread_args *args = malloc (sizeof (struct thread_args));
args->first = 5;
args->second = "Hello";

/* Note that the data structure resides on the heap */
assert (pthread_create (&child, NULL, hello_thread, args) == 0);
3) từ chính bản thân
/* Code Listing 6.8:
   A safer version of Code Listing 6.5
 */

/* Convention: It is common to name a void* parameter with a name
   that begins with _, then cast it to a local variable that has
   the same (or nearly the same) name without the _. So _args will
   become args. Recall that _ has no special meaning and is treated
   like a normal alphabetical character. */

void *
child_thread (void *_args)
{
  /* Safe whenever size of int <= size of pointer (which is
     usually true) */
  int arg = (int) _args;

  /* Print the local copy of the argument */
  printf ("Argument is %ld\n", arg);
  pthread_exit (NULL);
}
3

/* Code Listing 6.5:
   A thread that will print a single integer value
 */

void *
child_thread (void *args)
{
  /* POTENTIALLY DANGEROUS TIMING */
  int *argptr = (int *) args;
  int arg = *argptr;

  /* Print the local copy of the argument */
  printf ("Argument is %d\n", arg);
  pthread_exit (NULL);
}
4

/* Code Listing 6.5:
   A thread that will print a single integer value
 */

void *
child_thread (void *args)
{
  /* POTENTIALLY DANGEROUS TIMING */
  int *argptr = (int *) args;
  int arg = *argptr;

  /* Print the local copy of the argument */
  printf ("Argument is %d\n", arg);
  pthread_exit (NULL);
}
5

Có ba quan sát chính về phương pháp này

  • Các luồng chính và luồng con có quyền truy cập vào cả đầu vào và đầu ra. Thực tế này có nghĩa là luồng chính có thông tin về cách luồng con cụ thể này được gọi. Nếu luồng chính đang theo dõi nhiều luồng, thông tin bổ sung này có thể hữu ích
  • Trách nhiệm quản lý bộ nhớ nằm ở một vị trí. chủ đề chính. Nếu trách nhiệm được phân chia giữa lập trình viên duy trì luồng chính và lập trình viên duy trì luồng con, thì có khả năng xảy ra sai sót dẫn đến rò rỉ bộ nhớ (hoặc tệ hơn là phân bổ lại sớm)
  • Nhược điểm chính của phương pháp này là các tham số đầu vào có thể được giữ trên heap lâu hơn mức cần thiết, đặc biệt nếu luồng con chạy trong một khoảng thời gian đáng kể

hiển thị một cách tiếp cận khác cho các loại trả về vô hướng đơn giản, đó là sử dụng lại thủ thuật truyền tới và từ loại

/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
5. Khi một luồng gọi
/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
8, nó có thể chỉ định một con trỏ để trả về làm đối số

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21

/* Code Listing 6.5:
   A thread that will print a single integer value
 */

void *
child_thread (void *args)
{
  /* POTENTIALLY DANGEROUS TIMING */
  int *argptr = (int *) args;
  int arg = *argptr;

  /* Print the local copy of the argument */
  printf ("Argument is %d\n", arg);
  pthread_exit (NULL);
}
7

hiển thị cách luồng chính gọi ___0_______9 để truy xuất con trỏ. Trừ khi luồng đã được tách ra (hoặc nó đã được tạo bằng thuộc tính

/* Code Listing 6.9:
   Passing multiple arguments to a thread requires grouping them into a struct
 */

/* Assume we have:
     struct thread_args {
       int first;
       const char *second;
     };
 */

struct thread_args *args = malloc (sizeof (struct thread_args));
args->first = 5;
args->second = "Hello";

/* Note that the data structure resides on the heap */
assert (pthread_create (&child, NULL, hello_thread, args) == 0);
8), con trỏ được trả về với
/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
8 sẽ vẫn được liên kết với luồng cho đến khi nó được nối

/* Code Listing 6.5:
   A thread that will print a single integer value
 */

void *
child_thread (void *args)
{
  /* POTENTIALLY DANGEROUS TIMING */
  int *argptr = (int *) args;
  int arg = *argptr;

  /* Print the local copy of the argument */
  printf ("Argument is %d\n", arg);
  pthread_exit (NULL);
}
8

/* Code Listing 6.5:
   A thread that will print a single integer value
 */

void *
child_thread (void *args)
{
  /* POTENTIALLY DANGEROUS TIMING */
  int *argptr = (int *) args;
  int arg = *argptr;

  /* Print the local copy of the argument */
  printf ("Argument is %d\n", arg);
  pthread_exit (NULL);
}
9

hiển thị cách tiếp cận thứ ba để trả về các giá trị từ luồng. Theo kiểu này, luồng con phân bổ một ____________3 riêng biệt một cách linh hoạt để giữ các giá trị trả về. Kỹ thuật này cho phép một luồng trả về nhiều giá trị thay vì một đại lượng vô hướng. Chẳng hạn, hãy xem xét chủ đề

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
1 sau đây. Nó nhận hai giá trị
/* Code Listing 6.7:
   Each thread should be given a separate value, rather than a shared address
 */

/* FIXED VERSION */
/* ints are passed by value, so a COPY gets passed to each call */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, (void *)i) == 0);
8 làm đầu vào và trả về kết quả của năm phép tính số học đơn giản

/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
0

/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
1

Điều quan trọng cần lưu ý là thể hiện cấu trúc ở đây phải được phân bổ động. Khi luồng gọi ____0_______8, mọi thứ trên ngăn xếp của nó sẽ không hợp lệ. Một chuỗi không bao giờ được chuyển một con trỏ tới một biến cục bộ với ____0_______8

Truy xuất dữ liệu đã trả về có thể được thực hiện bằng

/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
9. Trong ví dụ sau, luồng chính tạo năm phiên bản riêng biệt của luồng
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
1. Mỗi luồng con này nhận một con trỏ tới một thể hiện
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
7 duy nhất với các tham số tương ứng. Sau đó, mỗi đứa trẻ phân bổ phiên bản
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
8 của riêng nó trên heap. Điều này cho phép dữ liệu tồn tại sau khi luồng kết thúc. Trong , luồng chính nhận từng con trỏ của luồng tại một thời điểm, với một lệnh gọi riêng tới
/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
9. Vì luồng con đã kết thúc tại thời điểm này, nên luồng chính phải chịu trách nhiệm gọi
/* Code Listing 6.10:
   The child thread receives multiple values through the passed struct
 */

/* Using the convention of casting _args to args */
void *
hello_thread (void *_args)
{
  /* Cast args into a meaningful pointer type that we can use */
  struct thread_args *args = (struct thread_args *) _args;
  printf ("First: %d; Second: '%s'\n", args->first, args->second);

  /* Do not forget to free the struct used for arguments */
  free (args);
  pthread_exit (NULL);
}
0 để phân bổ lại ví dụ kết quả
/* Code Listing 6.8:
   A safer version of Code Listing 6.5
 */

/* Convention: It is common to name a void* parameter with a name
   that begins with _, then cast it to a local variable that has
   the same (or nearly the same) name without the _. So _args will
   become args. Recall that _ has no special meaning and is treated
   like a normal alphabetical character. */

void *
child_thread (void *_args)
{
  /* Safe whenever size of int <= size of pointer (which is
     usually true) */
  int arg = (int) _args;

  /* Print the local copy of the argument */
  printf ("Argument is %ld\n", arg);
  pthread_exit (NULL);
}
3

/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
2

/* Code Listing 6.6:
   Passing a pointer to a variable that repeatedly changes is a common error with threads
 */

/* BAD CODE - DON'T DO THIS */
/* What value is actually passed to the thread? */
for (int i = 1; i <= 10; i++)
  assert (pthread_create (&child[i], NULL, child_thread, &i) == 0);
3

Cảnh báo lỗi


Tất cả các hàm để tạo luồng, truyền đối số và nhận giá trị trả về đều liên quan đến rất nhiều con trỏ. Hơn nữa, các con trỏ được hủy đăng ký và thao tác không đồng bộ do bản chất của đa luồng. Điều quan trọng là phải nhớ các loại và thời gian tồn tại của từng con trỏ và cấu trúc dữ liệu tương ứng

Làm cách nào để chuyển nhiều đối số cho hàm trong C?

Chỉ các dấu chấm lửng sẽ được sử dụng để truyền số lượng đối số thay đổi. .
Xác định một hàm với tham số cuối cùng của nó là dấu chấm lửng và tham số ngay trước dấu chấm lửng luôn là một int sẽ biểu thị số lượng đối số
Tạo một biến kiểu va_list trong định nghĩa hàm

Có thể truyền nhiều đối số cho một hàm không?

Một số hàm được thiết kế để trả về giá trị, trong khi những hàm khác được thiết kế cho các mục đích khác. Chúng ta truyền đối số trong một hàm, chúng ta có thể không truyền đối số nào, một đối số hoặc nhiều đối số cho một hàm và có thể gọi hàm nhiều lần.

Làm cách nào để chuyển số lượng đối số thay đổi cho một hàm trong C?

Để gọi một hàm có số lượng đối số thay đổi, chỉ cần chỉ định bất kỳ số lượng đối số nào trong lệnh gọi hàm . Một ví dụ là hàm printf từ thư viện thời gian chạy C. Lời gọi hàm phải bao gồm một đối số cho mỗi tên kiểu được khai báo trong danh sách tham số hoặc danh sách kiểu đối số.

Làm cách nào để vượt qua số lượng đối số chưa biết trong C?

Đặt dấu chấm lửng (ba dấu chấm) làm tham số cuối cùng mà bạn muốn 'số lượng tham số thay đổi . Và sau đó, bạn có một loại va_list đặc biệt cung cấp cho bạn danh sách các đối số được truyền và bạn có thể sử dụng các macro va_start , va_arg và va_end để lặp qua danh sách các đối số.