繁体   English   中英

C ++ UNIX线程

[英]C++ UNIX threading

我正在使用UNIX和C ++中的线程进行项目。 基本上有一个生产者线程和5个使用者线程。 生产者线程在随机时间将递增的数字添加到队列中,而消费者线程轮询q以尝试将其删除。 由于某种原因,我的q.size()一直为负值,我不知道为什么。

 #include <queue>
 #include <list>

 #include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
 #include <pthread.h>

 using namespace std;

 #define NUM_CONSUMER_THREADS 5
 #define NUM_PRODUCER_THREADS 1
 #define BUFFER_SIZE 20

 void *c_thread_function(void *arg);
 void *p_thread_function(void *arg);

 queue<int> q;

 int produce(int cur)
 {
  int temp = cur + 1;
  return temp;
 }

 void append(int num)
 {
  if ( q.size() < BUFFER_SIZE )
  {
   q.push(num);
  }
 }

 int take()
 {
  int removed = q.front();
  q.pop();
  sleep(1);
  return removed;
 }

 void consume(int num, int thread)
 {
  printf("%d consumed %d \n", thread, num);
 }


 int main() 
 {
  int result;

  pthread_t cthreads[NUM_CONSUMER_THREADS];
  pthread_t pthreads[NUM_PRODUCER_THREADS];

  void *thread_result;

  // build an array of consumer threads
  for(int num_of_cthreads = 0; num_of_cthreads < NUM_CONSUMER_THREADS; num_of_cthreads++) 
  {
   result = pthread_create(&(cthreads[num_of_cthreads]), NULL, c_thread_function, (void *)num_of_cthreads);
   if ( result != 0 )
   {
    perror( "Thread Creation Failed");
    exit(EXIT_FAILURE);
   }
   //sleep(1);  
  } 

  // build an array of producer threads
  for(int num_of_pthreads = 0; num_of_pthreads < NUM_PRODUCER_THREADS; num_of_pthreads++) 
  {
   result = pthread_create(&(pthreads[num_of_pthreads]), NULL, p_thread_function, NULL);
   if ( result != 0 )
   {
    perror( "Thread Creation Failed");
    exit(EXIT_FAILURE);
   }
   //sleep(1);  
  }

  printf("All threads created\n");
  while ( true )
  {
   // do nothing
  }
 }

 void *c_thread_function(void *arg)
 {
  int temp = (long)arg;
  printf("Consumer thread %d created \n", temp);

  while ( true )
  {
   while (  q.size() > 0 )
   {
    int w = take();
    consume(w, temp);
    printf(" q size is now %d \n", q.size());
   }
  }
 }

 void *p_thread_function(void *arg) 
 {
  printf("Producer thread created \n");

  int itemsAdded = 0;
  int temp;
  int sleepTime;

  while ( true ) 
  {
   while ( q.size() < BUFFER_SIZE )
   {
    temp = produce(itemsAdded);

    sleepTime = 1+(int)(9.0*rand()/(RAND_MAX+1.0));
    sleep(sleepTime);

    append(temp);

    printf("Producer adds: %d \n", temp);
    printf(" q size is now %d \n", q.size());

    itemsAdded++;
   }
  }
 }

输出:

生产者添加:1

q大小现在是-1

0消耗了1

q大小现在是-2

1消耗了1

q大小现在是-3

3已消耗1

q大小现在是-4

4已消耗0

q大小现在是-5

0消耗了0

您需要了解种族条件相互排斥的概念。 您的std::queue对象是共享资源 ,这意味着有多个线程正在该线程上运行-可能同时运行 这意味着您必须使用锁(称为互斥锁)来保护它,以便每次访问都同步。 否则,您将获得所谓的竞态条件 ,其中一个线程修改数据,而另一个线程也在访问/修改数据,从而导致程序状态不一致或损坏。

为了防止出现竞争状况,您需要在每次队列访问之前锁定pthread_mutex对象。

首先,您需要创建一个互斥对象并对其进行初始化。

pthread_mutex mymutex;
pthread_mutex_init(&mymutex, 0);

然后,您的应用程序代码应如下所示:

pthread_mutex_lock(&mymutex);

// Do something with queue

pthread_mutex_unlock(&mymutex);

当一个线程获取锁时,其他任何线程都无法获取锁。 试图获取已经被另一个线程获取的锁的线程将只等到释放该锁。 这将同步对队列的访问,确保一次只有一个线程对其进行修改。

STL容器(例如queue不是线程安全的。 您需要同步对队列对象的访问,例如,使用互斥锁。

几个问题:

  • 您正在忙着等待。
    学习使用条件变量。 因此,线程等待不使用资源。

  • int temp = (long)arg; 不上班。
    无法保证何时安排线程运行。
    指针arg指向一个很久以前可能已更改的变量。

  • 无论消费者/生产者线程修改阙q没有获得独占访问。
    任何其他线程都可以修改大小测试和添加材料点之间的队列。 更糟糕的是,另一个线程可能会同时尝试修改que(并且我相对确定STL对于修改不是线程安全的)。

尝试这样的事情:

#include <iostream>
#include <vector>
#include <queue>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>

#define CONSUMER_COUNT  5
#define PRODUCER_COUNT  2

第一部分线程需要的所有数据

struct ThreadQueue
{
    ThreadQueue()
        : finished(false)
    {
         if (pthread_mutex_init(&mutex, NULL) != 0)
         {  throw int(1);
         }
         if (pthread_cond_init(&cond, NULL) != 0)
         {
             // Technically we should wrap the mutext.
             // So if the condition variable fails it is
             // auto destroyed. This is left as an exercise.
             throw int(1);
         }
    }
    ~ThreadQueue()
    {
        if (pthread_cond_destroy(&cond) != 0)
        {   //throw int(1); // Do we really care?
        }
        if (pthread_mutex_destroy(&mutex) != 0)
        {   //throw int(1);
        }
    }
    std::queue<int>     data;
    pthread_mutex_t     mutex;
    pthread_cond_t      cond;
    bool                finished;
};

消费者线程

extern "C" void* consumerThread(void* arg)
{
    ThreadQueue&     que = *static_cast<ThreadQueue*>(arg);

    while(!que.finished)
    {
        // Get the lock before proceeding
        pthread_mutex_lock(&que.mutex);
        while(que.data.size() == 0)
        {
            // If there is no data in the que the sleep on the condition.
            pthread_cond_wait(&que.cond, &que.mutex);

            // We may have been released here because of a signal.
            // That does not mean we got out before one of the other
            // consumer threads already stoll the value from the queue.
            // So we must be in a loop and re-check the size() of the
            // que before we proceed. If the value was already stolen
            // then we go back to sleep waiting on the condition variable.

            if (que.finished)
                break;
        }

        // We have a lock with data in the que
        int value   = que.data.front();
        que.data.pop();

        // Use the same lock to access std::cout
        std::cout << "Consumer Got: " << value << "\n";
        pthread_mutex_unlock(&que.mutex);
    }
    return NULL;
}

生产者线程

extern "C" void* producerThread(void* arg)
{
    ThreadQueue&     que = *static_cast<ThreadQueue*>(arg);

    while(!que.finished)
    {
        // Get the lock before proceeding
        pthread_mutex_lock(&que.mutex);

        // Add a new value to the queue
        int value = rand();
        que.data.push(value);

        // Ise the same lock to access std::cout
        std::cout << "Producer Push: " << value << "\n";

        // Signal a consumer to be released.
        pthread_cond_signal(&que.cond);

        // rand maintains internal state.
        // calls to rand() should therefore be protected by a mutex.
        // Again in this simple example we re-use the same mutex for protection
        int sleepTime = rand() % 5;

        // Now release the lock
        pthread_mutex_unlock(&que.mutex);
        sleep(sleepTime);
    }
    return NULL;
}

主循环

int main()
{
    srand(time(NULL));

    ThreadQueue     queue;
    pthread_t       consumerThreads[CONSUMER_COUNT];
    pthread_t       producerThreads[PRODUCER_COUNT];

    try
    {
      for(int loop=0 ;loop < CONSUMER_COUNT; ++loop)
      {
          if (pthread_create(&consumerThreads[loop], NULL, consumerThread, &queue) != 0)
          {   throw int(2);
          }
      }
      for(int loop=0 ;loop < PRODUCER_COUNT; ++loop)
      {
          if (pthread_create(&producerThreads[loop], NULL, producerThread, &queue) != 0)
          {   throw int(3);
          }
      }
   }
   catch(...)
   {
       // Set the finished to true so all threads exit.
       queue.finished = true;
       // Some consumers may be waiting on the condition.
       // So wake them up one signal per consumer should do it.
       for(int loop = 0;loop < CONSUMER_COUNT; ++loop)
       {    pthread_cond_signal(&queue.cond);
       }
    }

    /* Wait for all threads to finish */
    for(int loop=0; loop < CONSUMER_COUNT; ++loop)
    {
        pthread_join(consumerThreads[loop], NULL);
    }
    for(int loop=0; loop < PRODUCER_COUNT; ++loop)
    {
        pthread_join(producerThreads[loop], NULL);
    }
};

霍普,我说对了:-)

我发现了问题...

任务的重点是显示没有信号量的线程是多么不可靠。 这是我需要修复的代码...

int take()
{
 int removed = q.front();
 sleep(1); // switched
 q.pop();  // these two...
 return removed;
}

我还从生产者线程中删除了睡眠计时器。 现在一切正常...

输出现在执行此操作:

---Producer adds: 1 ---
---Producer adds: 2 ---
---Producer adds: 3 ---
---Producer adds: 4 ---
---Producer adds: 5 ---
---Producer adds: 6 ---
---Producer adds: 7 ---
---Producer adds: 8 ---
---Producer adds: 9 ---
---Producer adds: 10 ---
---Producer adds: 11 ---
---Producer adds: 12 ---
---Producer adds: 13 ---
---Producer adds: 14 ---
---Producer adds: 15 ---
---Producer adds: 16 ---
---Producer adds: 17 ---
---Producer adds: 18 ---
---Producer adds: 19 ---
---Producer adds: 20 ---
Thread 3 consumed 1 
Thread 1 consumed 1 
Thread 2 consumed 1 
Thread 4 consumed 1 
Thread 0 consumed 1 
---Producer adds: 21 ---
---Producer adds: 22 ---
---Producer adds: 23 ---
---Producer adds: 24 ---
---Producer adds: 25 ---
Thread 3 consumed 6 
Thread 4 consumed 6 
Thread 1 consumed 6 
---Producer adds: 26 ---
---Producer adds: 27 ---
---Producer adds: 28 ---
---Producer adds: 29 ---
---Producer adds: 30 ---
Thread 0 consumed 6 
Thread 2 consumed 6 
Thread 3 consumed 11 
Thread 4 consumed 11    

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM