繁体   English   中英

在 C++ 中使用线程交替打印奇数和偶数打印

[英]printing odd and even number printing alternately using threads in C++

使用线程打印奇偶数我遇到了这个问题,想讨论 C++ 中的解决方案。 我能想到的使用 2 个二进制信号量奇数和偶数信号量。 偶数信号量初始化为 1,奇数信号量初始化为 0。

**T1 thread function** 
funOdd()
{  
  wait(even)  
  print odd;  
  signal(odd)  
}


**T2 thread function**
funEven()  
{  
  wait(odd)  
  print even  
  signal(even)  
}  

除此之外,如果我的函数只生成数字并且有第三个线程 T3 将打印这些数字,那么理想的设计应该是什么? 我使用了一个数组,其中奇数将放置在奇数位置,偶数将放置在偶数位置。 T3 将从这个数组中读取,这将避免对该数组的任何线程安全,如果 T3 没有找到任何索引,那么它将等待该索引被填充。 另一种解决方案是使用一个队列,该队列将有一个互斥锁,T1 和 T2 在插入时可以使用该互斥锁。

请对此解决方案发表评论,以及如何使其更有效率。

编辑以使问题更清楚:总体问题是我有两个生产者(T1,T2)和一个消费者(T3),并且我的生产者是相互依赖的。

使用条件变量

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mu;
std::condition_variable cond;
int count = 1;

void PrintOdd()
{
    for(; count < 100;)
    {
        std::unique_lock<std::mutex> locker(mu);
        cond.wait(locker,[](){ return (count%2 == 1); });
        std::cout << "From Odd:    " << count << std::endl;
        count++;
        locker.unlock();
        cond.notify_all();
    }

}

void PrintEven()
{
    for(; count < 100;)
    {
        std::unique_lock<std::mutex> locker(mu);
        cond.wait(locker,[](){ return (count%2 == 0); });
        std::cout << "From Even: " << count << std::endl;
        count++;
        locker.unlock();
        cond.notify_all();
    }
}

int main()
{
    std::thread t1(PrintOdd);
    std::thread t2(PrintEven);
    t1.join();
    t2.join();
    return 0;
}

使用条件变量的解决方案。

#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
mutex oddevenMu;
condition_variable condVar;
int number = 1;

void printEvenOdd(bool isEven, int maxnubmer)
{
    unique_lock<mutex> ul(oddevenMu);
    while (number < maxnubmer)
    {
        condVar.wait(ul, [&]() {return number % 2 == isEven;});
        cout << number++ << " ";
        condVar.notify_all();
    }

}

int main(string args[])
{
    thread oddThread(printEvenOdd, false, 100);
    thread evenThread(printEvenOdd, true, 100);
    oddThread.join();
    evenThread.join();
    return 0;
}

解决方案基于 C++11 关键代码部分又名mutex

这是工作代码,然后是解释。

在 VS2013 上测试和工作:

using namespace std;
#include <iostream>
#include <string>
#include <thread>
#include <mutex>

std::mutex mtx;

void oddAndEven(int n, int end);

int main()
{
std::thread odd(oddAndEven, 1, 10);
std::thread Even(oddAndEven, 2, 10);

odd.join();
Even.join();

return 0;
}



void oddAndEven(int n, int end){
int x = n;
for (; x < end;){
    mtx.lock();
    std::cout << n << " - " << x << endl;
    x += 2;
    mtx.unlock();
    std::this_thread::yield();
    continue;
 }
}

IE:

线程奇数转到方法oddAndEven,起始编号为1,因此他是奇数。 他是第一个获得mtx.lock()锁的人。

同时,线程Even 也试图获取锁,但线程奇数首先获取它,所以线程Even等待。

回到奇数线程(有锁),他打印数字 1 并使用mtx.unlock()释放锁。 此时,我们希望线程Even获取锁并打印 2 所以我们通过编写std::this_thread::yield()通知线程Even 然后线程Even做同样的事情。

等等等等

这是您可以参考的最简单的解决方案:

#include<iostream>
#include<mutex>
#include<pthread.h>
#include<cstdlib>
int count=0;
using namespace std;
mutex m;
void* printEven(void *a)
{
   while(1)
   {
       m.lock();
       if(count%2==0)
       {
          cout<<" I am Even"<<count<<endl;
          count++;
       }
       if(count==100)
           break;
       m.unlock();
   }
}
void* printOdd(void *b)
{
    while(1)
    {
       m.lock();
       if(count%2!=0)
       {
           cout<<"I am odd"<<count<<endl;
           count++;
       }
       if(count>100)
          break;
       m.unlock();
    }
 }
 int main()
 {
     int *ptr = new int();
     pthread_t thread1, thread2;
     pthread_attr_t attr;
     pthread_attr_init(&attr);
     pthread_create(&thread1,&attr,&printEven,NULL);
     pthread_create(&thread2,&attr,&printOdd, NULL);
     pthread_join(thread1,&ptr);
     pthread_join(thread2,&ptr);
     delete ptr;
 }

这是使用单一功能的简单解决方案。

#include <iostream>
#include <thread>
#include <condition_variable>
using namespace std;

mutex mu;
condition_variable cond;
int count = 1;

void PrintOddAndEven(bool even, int n){
    while(count < n){
        unique_lock<mutex> lk(mu);
        cond.wait(lk, [&](){return count%2 == even;});
        cout << count++ << " ";
        lk.unlock();
        cond.notify_all();
    }
}

int main() {
    int n = 10;
    thread t1(PrintOddAndEven, true, n);
    thread t2(PrintOddAndEven, false, n);

    t1.join();
    t2.join();
    return 0;
}
    #include <iostream>
    #include <thread>
    #include <mutex> 
    using namespace std;

    std::mutex m;
    int count = 0;

    void printEven()
    {
        cout << "Entered Even\n" << endl;
        while(count <= 10)
        {
            m.lock();
            if(count%2 == 0)
                cout << count++ << " ";
             m.unlock();
        }
    }
    
    void printOdd()
    {
        cout << "Entered Odd" << endl;
        while(count < 10)
        {
             m.lock();
            if(count%2 == 1)
                cout << count++ << " ";
             m.unlock();
        }
    }

    int main()
    {
       std::thread t1(printOdd);
       std::thread t2(printEven);
       t1.join();
       t2.join();
        return 0;
    }

我不明白为什么要使用三个单独的线程来实现串行行为。 但无论如何我都会回答:)

一种解决方案是使用经过修改的生产者/消费者模式,并在生产者和消费者之间设置优先队列 队列上的排序操作将取决于发布消息的整数值。 消费者会查看队列中的一个元素并检查它是否是下一个预期元素。 如果没有,它将休眠/等待。

一点代码:

class Elt implements Comparable<Elt> {
  int value;
  Elt(value) { this.value=value; }
  int compare(Elt elt);
}

class EltQueue extends PriorityBlockingQueue<Elt> { // you shouldn't inherit colelctions, has-a is better, but to make it short
  static EltQueue getInstance(); // singleton pattern
}

class Consumer{
  Elt prevElt = new Elt(-1);
  void work()
  {
    Elt elt = EltQueue.getInstance().peek();
    if (elt.getValue() == prevElt.getValue()+1)) {
      EltQueue.getInstance().poll();
      //do work on Elt
    }
  }
}

class Producer {
  int n=0; // or 1!
  void work() {
    EltQueue.getInstance().put(new Elt(n+=2));
  }
}

首先,这两个函数应该至少包含一个循环,(除非你只想要一个数字)

更标准的解决方案(重新映射您的想法)是拥有一个包含互斥锁和两个条件变量(奇数和偶数)加上返回值和另一个打印条件的全局结构。 而不是使用 uique_lock 来处理同步。

在伪代码中:

struct global_t
{
    mutex mtx;
    int value = {0};
    condition_variable be_odd, be_even, print_it;
    bool bye = {false};

    global_t() { be_odd.notify(); }
} global;

void odd_generator()
{
    int my_odd = 1;
    for(;;)
    {
        unique_lock lock(global.mtx);
        if(global.bye) return;
        global.be_odd.wait(lock);
        global_value = my_odd; my_odd+=2;
        global.print_it.notify();
        if(my_odd > 100) bye=true;
    } //let RAII to manage wait states and unlocking
};

void even_generator()
{ /* same as odd, with inverted roles */ }

void printer()
{
    for(;;)
    {
        unique_lock lock(global.mtx);
        if(bye) return;
        global.ptint_it.wait(lock);
        std::cout << global.value << std::endl;
        ((global.value & 1)? global.be_even: global.be_odd).notify();
    }
}


int main()
{
    thread oddt(odd_generator), event(even_generator), printt(printer);
    oddt.join(), event.join(), printer.join();
}

请注意,除了教学目的之外,该解决方案对打印计数器值的简单循环没有增加任何价值,因为永远不会有真正的并发。

还要注意(为了避免全局变量),您可以将所有内容包装到一个类中(使实际的 main 成为一个类方法)并在新 main 内的堆栈上实例化该类。

 #include  <stdio.h>
 #include  <stdlib.h>
 #include  <iostream>
 #include  <pthread.h>
 #include  <semaphore.h>

  sem_t sem;
  sem_t sem2;
  using namespace std ;

int count = 1;

void increment(int x)
{
    cout << "called by thread : " << x << "count is : " << count ++ << "\n";
}

void *printAltmessage1(void *thread_value)
{
    for(int m=0; m < (*(int *)thread_value); m++)
    {
        if (sem_wait(&sem) == 0)
        {
            cout << " Thread printAltmessage1 is executed" <<"\n";  
            increment(1);
            sem_post(&sem2);
        }
    }
}

void *printAltmessage2(void *thread_value)
{
    for(int m=0; m < (*(int *)thread_value); m++)
    {
        if (sem_wait(&sem2) == 0)
        {
            cout << " Thread printAltmessage2 is executed" <<"\n";
            increment(2);  
            sem_post(&sem);
        }
    }
}

int main()
{
     sem_init(&sem,0, 1);
     sem_init(&sem2,0, 0);
     pthread_t threads[2];
     int x =8;
     for(int i=0;i<2;i++)
     {
          if(i==0)
          int rc =pthread_create(&threads[i],NULL,printAltmessage1,(void*)&x);
          else
          int rc =pthread_create(&threads[i],NULL,printAltmessage2,(void*)&x);
      }
      pthread_exit(NULL);
      return 0;
}
#include <iostream>
#include <thread>
#include <mutex>

std::mutex mu;
unsigned int change = 0;

void printConsecutiveNumbers(int start, int end,unsigned int consecutive)
{
    int x = start;
    while (x < end)
    {
        //each thread has check there time is coming or not
        if (change % consecutive == start)
        {
            std::unique_lock<std::mutex> locker(mu);
            std::cout << "Thread " << start << " -> " << x << std::endl;
            x += consecutive;
            change++;
            //to counter overflow
            change %= consecutive;
        }
    }
}

int main()
{
    //change num = 2 for printing odd and even
    const int num = 7;
    const int endValue = 1000;
    std::thread threads[num];
    //Create each consecutive threads
    for (int i = 0; i < num; i++)
    {
        threads[i] = std::thread(printConsecutiveNumbers, i, endValue, num);
    }

    //Joins all thread to the main thread
    for (int i = 0; i < num; i++)
    {
        threads[i].join();
    }

    return 0;
}

此代码将起作用。 我已经在 Visual Studio 2017 上测试过了

#include "stdafx.h"
#include <iostream>
#include <mutex>
#include <thread>
#include <condition_variable>
using namespace std;
mutex m;
condition_variable cv;
int num = 1;


void oddThread() {​​​​​​​
    
    for (; num < 10;) {​​​​​​​
        unique_lock<mutex> lg(m);
        cv.wait(lg, [] {​​​​​​​return (num % 2 ==1); }​​​​​​​);
        cout << "From odd Thread " << num << endl;
        num++;
        lg.unlock();
        cv.notify_one();
    }​​​​​​​
}​​​​​​​
void evenThread() {​​​​​​​
    for (; num < 100;) {​​​​​​​
        unique_lock<mutex> lg(m);
        cv.wait(lg, [] {​​​​​​​return (num % 2 == 0); }​​​​​​​);
        cout << "From even Thread " << num << endl;
        num++;
        lg.unlock();
        cv.notify_one();
    }​​​​​​​
}​​​​​​​


int main() {​​​​​​​
    
    thread t1{​​​​​​​ oddThread}​​​​​​​; //odd function thread
    thread t2{​​​​​​​ evenThread}​​​​​​​;
    t1.join();
    t2.join();
    cin.get();
    return 0;
}​​​​​​​

输出

来自oddThread:1来自evenThread:2来自oddThread:3来自evenThread:4来自oddThread:5来自evenThread:6来自oddThread:7来自evenThread:8来自oddThread:9来自evenThread:10

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;
std::mutex m;
std::condition_variable cv;
int counter =1;

void printEven()
{
    std::unique_lock<std::mutex> lk(m);
    while(1)
    {
        if(counter > 10)
            break;
        if(counter %2 != 0)
        {
            cv.wait (lk);
        }
        else
        {
            cout << "counter : " << counter << endl;
            counter++;
            //lk.unlock();
            cv.notify_one();
        }
    
    
    }
}


void printOdd()
{
    std::unique_lock<std::mutex> lk(m);
    
    while(1)
    {
        if(counter > 9)
        break;
        if(counter %2 == 0)
        {
            cv.wait (lk);
        }
        else
        {
            cout << "counter : " << counter << endl;
            counter++;
            //lk.unlock();
            cv.notify_one();
        }
    
    
    }
}

int main()
{
    std::thread t1(printEven);
    std::thread t2(printOdd);
    t1.join();
    t2.join();
    cout << "Main Ends" << endl;
}

我已经使用匿名函数(lambda)来做到这一点,并使用棒状 cond 变量和互斥锁。

#include <iostream>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <chrono>

using namespace std; 

int main() {
    int count = 1;
    mutex mtx;
    condition_variable condition;
    const int ITERATIONS = 20; // iterations

    //prints odd numbers
    thread t1([&]() {
        while (count < ITERATIONS)
        {
            unique_lock <mutex> lock(mtx);
            condition.wait(lock, [&]() {
                return count % 2 != 0;
                });

            cout << "thread1 prints: " << count << endl;
            count++;
            lock.unlock();
            condition.notify_all();
        }
    });
    
    thread t2([&]
        {
            while (count < ITERATIONS)
            {
                unique_lock <mutex> lock(mtx);
                condition.wait(lock, [&]() {
                    return count % 2 == 0;
                    });
                cout << "thread2 prints: " << count << endl;
                count++;
                lock.unlock();
                condition.notify_all();
            }
        });

    t1.join();
    t2.join();
 }
#include "threadFunc.hpp"
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

using namespace std;

mutex t1;
condition_variable cond;


int number = 11;
int count = 0;

void printEven()
{
    while(1)
    {
        unique_lock<mutex> ul(t1);
        if(count< number)
        {
            if(count % 2 != 0)
            {
                cond.wait(ul);
            }

            cout<<count<<" : printed by thread"<<this_thread::get_id()<<endl;
            count++;
        }
        if(count > number)
            break;

        ul.unlock();
        cond.notify_all();
    }

}

void printOdd()
{
    while(1)
    {
        unique_lock<mutex> ul(t1);
        if(count< number)
        {
            if(count % 2 == 0)
            {
                cond.wait(ul);
            }

            cout<<count<<" : printed by thread"<<this_thread::get_id()<<endl;
            count++;
        }
        if(count > number)
            break;

        ul.unlock();
        cond.notify_all();
    }

}
#include <bits/stdc++.h>
#include <stdlib.h>
#include <unistd.h>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;

mutex m;
condition_variable cv;
unique_lock<mutex> lck(m);

void *printeven(void *arg)
{
    int *n = (int *)arg;
    while (*n <= 100)
    {
        cv.wait(lck);
        *n = *((int *)arg);
        cout << this_thread::get_id() << " : " << *n << endl;
        *n = *n + 1;
        cv.notify_one();
    }
    exit(0);
}

void *printodd(void *arg)
{
    int *n = (int *)arg;
    while (*n <= 100)
    {
        *n = *((int *)arg);
        cout << this_thread::get_id() << " : " << *n << endl;
        *n = *n + 1;
        cv.notify_one();
        cv.wait(lck);
    }
    exit(0);
}

int main()
{
    int num = 1;
    pthread_t p1 = 1;
    pthread_t p2 = 2;

    pthread_create(&p1, NULL, printodd, &num);
    pthread_create(&p2, NULL, printeven, &num);

    pthread_join(p1, NULL);
    pthread_join(p2, NULL);
    return 0;
}
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mtx;
std::condition_variable even_to_odd;
std::condition_variable odd_to_even;
unsigned int count = 1;
constexpr int MAX_COUNT = 20;

void even(int n) {
    for (int i = n; i <= MAX_COUNT; i++) {
        std::unique_lock<std::mutex> lock(mtx);
        if (i %2 == 0) {
            odd_to_even.wait(lock);
            std::cout << "Even: " << i << std::endl;
            even_to_odd.notify_one();
        }
    }
}

void odd(int n) {
    for (int i = n; i <= MAX_COUNT; i++) {
        std::unique_lock<std::mutex> lock(mtx);
        if (i == 1) {
            std::cout << "Odd : " << i << std::endl;
            odd_to_even.notify_one();
        }
        else if (i % 2 != 0) {
            even_to_odd.wait(lock);
            std::cout << "Odd : " << i << std::endl;
            odd_to_even.notify_one();
        }
    }
}

int main() {
    std::thread thread1(even,count);
    std::thread thread2(odd,count);

    thread1.join();
    thread2.join();
    return 0;
}

请参阅下面的工作代码(VS2005)

#include <windows.h>
#include <stdlib.h>

#include <iostream>
#include <process.h>

#define MAX 100
int shared_value = 0;

CRITICAL_SECTION cs;

unsigned _stdcall even_thread_cs(void *p)
{

    for( int i = 0 ; i < MAX ; i++ )
    {
        EnterCriticalSection(&cs);

        if( shared_value % 2 == 0 )
        {
            printf("\n%d", i);
        }


        LeaveCriticalSection(&cs);

    }
    return 0;
}

unsigned _stdcall odd_thread_cs(void *p)
{
    for( int i = 0 ; i < MAX ; i++ )
    {
        EnterCriticalSection(&cs);

        if( shared_value % 2 != 0 )
        {
            printf("\n%d", i);
        }

        LeaveCriticalSection(&cs);  

    }

    return 0;
}


int main(int argc, char* argv[])
{
     InitializeCriticalSection(&cs);

    _beginthreadex(NULL, NULL, even_thread_cs, 0,0, 0);
    _beginthreadex(NULL, NULL, odd_thread_cs, 0,0, 0);

    getchar();
    return 0;
}

在这里,使用共享变量shared_value ,我们正在同步even_thread_csodd_thread_cs 请注意,不使用睡眠。

暂无
暂无

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

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