PThreads - one of two threads doesn't run

Hi,

my goal is to implement two threads. One running a vector of numbers upwards, the other one running the vector downwards. Both should set numbers in this vector to zero. So I use a global variable vector which should be mutexed.

When I run the following code just the second threads runs. When I slightly modify the data structure of the delivered arguments to the threads to a static object (i.e. setting thread data as a static array), the code works (see second code snippet). Why is that so?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include <iostream>
#include <vector>
#include <cmath>
#include <numeric>
#include <pthread.h>
using namespace std;

const unsigned long MAX_NUMBER = 1000000;
vector<bool> numbers(MAX_NUMBER+1);
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
unsigned long l1,l2;

// thread specific arguments
struct thread_data
{
  unsigned int thread_id;
  unsigned long n; // data to sieve
  bool bw; // backward or forward search
};

void init_numbers(vector <bool> * numbers);

void *prime_sieve(void *arg);

void init_numbers(vector <bool> * numbers)
{
  unsigned long i;
  (*numbers)[2]=true;
  for (i=3;i<=MAX_NUMBER;i+=2){(*numbers)[i]=true;}
}

void *prime_sieve(void *arg)
{
  struct thread_data *my_data;

  my_data = (struct thread_data *)arg;

  unsigned long i;
  unsigned long c;
  unsigned long pos_move;

  if (!(my_data->bw))
    {
     while (l1 <= l2)
      {
      if (numbers[l1] != false)
        {
          c = l1*l1;
          pos_move = (l1 << 1);
          while (c <= my_data->n)
            {
              pthread_mutex_lock(&mutex1);
              numbers[c]=false;
              pthread_mutex_unlock(&mutex1);
              c += pos_move;
            }
        }
       pthread_mutex_lock(&mutex1);
       l1++;
       cout << "l1 " << l1 << endl;
       pthread_mutex_unlock(&mutex1);
     }
    }
    else
      {
      while(l2 >= l1)
      {
      if (numbers[l2] != false)
        {
          c = l2*l2;
          pos_move = (l2 << 1);
          while (c <= my_data->n)
            {
              pthread_mutex_lock(&mutex1);
              numbers[c]=false;
              pthread_mutex_unlock(&mutex1);
              c += pos_move;
            }
        }
      pthread_mutex_lock(&mutex1);
      l2--;
      cout << "l2 " << l2 << endl;
      pthread_mutex_unlock(&mutex1);
      }
    }
  pthread_exit(NULL);
}

unsigned long get_primes(vector <bool> numbers, unsigned long n)
{
  unsigned long primes=0;
  for(unsigned int i=0;i<=n;i++)
  {
    primes+=numbers[i];
  }
  return primes;
}

int main(int argc, char *argv[])
{
  pthread_t threads[2];
  int rc;
  unsigned long n = MAX_NUMBER;
  init_numbers(&numbers);

  l1 = 2;
  l2 = (int)(sqrt(n))+1;

  struct thread_data my_data;
  my_data.thread_id = 0;
  my_data.n = MAX_NUMBER;
  my_data.bw = false;
  rc = pthread_create(&threads[0], NULL, prime_sieve, (void *) &my_data);

  struct thread_data my_data2;
  my_data2.thread_id = 1;
  my_data2.n = MAX_NUMBER;
  my_data.bw = true;
  rc = pthread_create(&threads[1], NULL, prime_sieve, (void *) &my_data2);

  void* retval;
  pthread_join(threads[0], &retval);
  pthread_join(threads[1], &retval);

  cout << get_primes(numbers, n) << endl;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130

#include <iostream>
#include <vector>
#include <cmath>
#include <numeric>
#include <pthread.h>
using namespace std;

const unsigned long  MAX_NUMBER = 1000000;
vector<bool> numbers(MAX_NUMBER+1);
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
unsigned long l1,l2;

// thread specific arguments
struct thread_data
{
  unsigned int thread_id;
  unsigned long n; // data to sieve
  bool bw; // backward or forward search
};

struct thread_data thread_data_array[2];

void init_numbers(vector <bool> * numbers);

void *prime_sieve(void *arg);

void init_numbers(vector <bool> * numbers)
{
  unsigned long i;
  (*numbers)[2]=true;
  for (i=3;i<=MAX_NUMBER;i+=2){(*numbers)[i]=true;}
}

void *prime_sieve(void *arg)
{
  struct thread_data *my_data;

  my_data = (struct thread_data *)arg;

  unsigned long i;
  unsigned long c;
  unsigned long pos_move;

  if (!(my_data->bw))
    {
     while (l1 <= l2)
      {
      if (numbers[l1] != false)
        {
          c = l1*l1;
          pos_move = (l1 << 1);
          while (c <= my_data->n)
            {
              pthread_mutex_lock(&mutex1);
              numbers[c]=false;
              pthread_mutex_unlock(&mutex1);
              c += pos_move;
            }
        }
       pthread_mutex_lock(&mutex1);
       l1++;
       cout << "l1 " << l1 << endl;
       pthread_mutex_unlock(&mutex1);
     }
    }
    else
      {
      while(l2 >= l1)
      {
      if (numbers[l2] != false)
        {
          c = l2*l2;
          pos_move = (l2 << 1);
          while (c <= my_data->n)
            {
              pthread_mutex_lock(&mutex1);
              numbers[c]=false;
              pthread_mutex_unlock(&mutex1);
              c += pos_move;
            }
        }
      pthread_mutex_lock(&mutex1);
      l2--;
      cout << "l2 " << l2 << endl;
      pthread_mutex_unlock(&mutex1);
      }
    }
  pthread_exit(NULL);
}

unsigned long get_primes(vector <bool> numbers, unsigned long n)
{
  unsigned long primes=0;
  for(unsigned int i=0;i<=n;i++)
  {
    primes+=numbers[i];
  }
  return primes;
}

int main(int argc, char *argv[])
{
  pthread_t threads[2];
  int rc;
  unsigned long n = MAX_NUMBER;
  init_numbers(&numbers);

  l1 = 2;
  l2 = (int)(sqrt(n))+1;

  thread_data_array[0].thread_id = 0;
  thread_data_array[0].n = MAX_NUMBER;
  thread_data_array[0].bw = false;
  rc = pthread_create(&threads[0], NULL, prime_sieve, (void *) &thread_data_arr\
ay[0]);

  thread_data_array[1].thread_id = 1;
  thread_data_array[1].n = MAX_NUMBER;
  thread_data_array[1].bw = true;
  rc = pthread_create(&threads[1], NULL, prime_sieve, (void *) &thread_data_arr\
ay[1]);

  void* retval;
  pthread_join(threads[0], &retval);
  pthread_join(threads[1], &retval);

  cout << get_primes(numbers, n) << endl;
}

Last edited on
Both threads run in both cases (place an output statement at the beginning of prime_sieve(), to see for yourself), but in the first case, the first thread was able to run to completion before the second thread ever got scheduled to run. In the second case, the first thread took a little longer to execute, and the second thread started working before the first one finished.

In addition, your threads are not synchronized on l1 or l2 or on the elements of numbers - the mutexes present do nothing useful, except for synchronizing the two output statements.
Last edited on
As an aside, whilst there's nothing at all wrong with pthreads, C++ now comes with threads, which are actually quite pleasant to work with. Here's some examples:

http://solarianprogrammer.com/2011/12/16/cpp-11-thread-tutorial/
Topic archived. No new replies allowed.