四个线程写四个文件

10 篇文章 0 订阅
题目:
有四个线程1、2、3、4。线程1的功能就是输出1,线程2的功能就是输出2,以此类推.........现在有四个文件ABCD。初始都为空。现要让四 个文件呈如下格式:A:1 2 3 4 1 2....B:2 3 4 1 2 3....C:3 4 1 2 3 4....D:4 1 2 3 4 1....请设计程序。

解答:
我的思路是这样的:
当每个子线程准备就绪后,主线程发出前进一步的命令【举旗】。这时每个子线程都收到该命令,找到正确的文件,打印自己的编号。然后向主线程报告自己已经完成任务,准备接受主线程的下一个命令。
这里的要点是每个子线程收到命令【举旗】后,该命令必须结束【放旗】。否则子线程完成该命令后会继续执行该命令。
第一次尝试:
使用自动重置事件(Event)作为主线程的旗子,举旗后,等待的四个线程跑起来。
但是这种方案不行,因为任何一个线程跑起来之后,旗子就放下了,导致其他线程跑不起来。
如果使用手工重置事件作为主线程的旗子,又找不到合适的机会放下旗子。
 
第二次尝试:
把Event改成Semaphore,让4个等待线程可以跑。尝试成功。
#include   <Windows.h>
#include   <iostream>
#include   <fstream>
#include   <process.h>
using   namespace   std;

const   int   thread_count=4;
HANDLE hGo;  //semaphore, used by main thread to notify all writer threads to go one step, like a traffic light
HANDLE hRoundDone[thread_count];  // Automatic Reset Event,    used by writer thread to notify main thread to show it finished this step
ofstream files[thread_count];

int  round=0;
const   int   max_round = 10;

void  threadfunc( void   * pi)
{
                   int  i = *(( int   *)pi);
                   int  fileId = (i+3)%thread_count;
                   while  (round < max_round)
                {
                                SetEvent(hRoundDone[i-1]);
                                WaitForSingleObject(hGo, INFINITE);
                                files[fileId] << i <<  " "  ;
                                fileId = (fileId+3)%thread_count;
                }
}


int  main()
{
                hGo = CreateSemaphore(   nullptr  , 0, thread_count,  nullptr   );
                   int  values[thread_count] = {1,2,3,4};
                   const   char   * filename[thread_count] = {  "A.txt" "B.txt"   "C.txt"   "D.txt"  };
                   for  ( int   i=0; i<thread_count; ++i)
                {
                                hRoundDone[i] = CreateEvent(   nullptr  , FALSE, FALSE,  nullptr   );
                                files[i].open(filename[i], ios_base::trunc);
                                _beginthread(threadfunc, 0, values+i);
                }
                   for  (; round<max_round; ++round)
                {
                                cout <<  "Main thread is waiting..."   << endl;
                                   // wait for all writer thread are ready
                                   WaitForMultipleObjects(thread_count, hRoundDone, TRUE, INFINITE);
                                ReleaseSemaphore(hGo, thread_count, NULL);   // increase count by thread_count
                                cout <<  "round="  << round << endl;
                }
                CloseHandle(hGo);
                   for  ( int   i=0; i<4; ++i)
                                CloseHandle(hRoundDone[i]);
                   return  0;
}

但是我有一个担心,当举旗时,4个线程至少有一个线程不在等待该旗子时【因为线程调度不可预知】,这样某个线程在某次命令中会不会多跑一圈?而那个掉队的线程就跑不了,导致死锁。
试验:把threadfunc改成下面的代码,即让SetEvent和 WaitForSingleObject有个不确定的间隔。跑了七八下, 结果都正确。但是再跑一下,死锁真的发生了。所以这个现象是比较难以重复的。
#include   <time.h>
void  threadfunc( void   * pi)
{
                srand( (  unsigned  )time( NULL ) );
                   int  i = *(( int   *)pi);
                   int  fileId = (i+3)%thread_count;
                   while  (round < max_round)
                {
                                SetEvent(hRoundDone[i-1]);
                                Sleep(rand()%100);
                                WaitForSingleObject(hGo, INFINITE);
                                files[fileId] << i <<  " "  ;
                                fileId = (fileId+3)%thread_count;
                }
}
  

第三次尝试:
把Semaphore改成4个自动重置事件(Event)。打个比方,四个车道,每个车道一个车(线程),每个车道有自己的红绿灯(自动重置事件),灯变绿,车子跑起来,并且灯立即变红。这样不会出现某个车跑2圈,而另外的某车跑不了的情况。
尝试也成功了。但我认为这个可靠多了。缺点是多用了一些同步对象。但正确性总是第一位的。
#include   <Windows.h>
#include   <iostream>
#include   <fstream>
#include   <process.h>
using   namespace   std;

const   int   thread_count=4;
HANDLE hGo[4];  //Automatic Reset Event, used by main thread to notify all writer threads to go one step, like a traffic light
HANDLE hRoundDone[thread_count];  //Automatic Reset Event,  used by writer thread to notify main thread to show it finished this step
ofstream files[thread_count];

int  round=0;
const   int   max_round = 10;

void  threadfunc( void   * pi)
{
                   int  i = *(( int   *)pi);
                   int  fileId = (i+3)%thread_count;
                   while  (round < max_round)
                {
                                SetEvent(hRoundDone[i-1]);
                                WaitForSingleObject(hGo[i-1], INFINITE);
                                files[fileId] << i <<  " "  ;
                                fileId = (fileId+3)%thread_count;
                }
}


int  main()
{
                   int  values[thread_count] = {1,2,3,4};
                   const   char   * filename[thread_count] = {  "A.txt" "B.txt"   "C.txt"   "D.txt"  };
                   for  ( int   i=0; i<thread_count; ++i)
                {
                                hGo[i] = CreateEvent(   nullptr  , FALSE, FALSE,  nullptr   );
                                hRoundDone[i] = CreateEvent(   nullptr  , FALSE, FALSE,  nullptr   );
                                files[i].open(filename[i], ios_base::trunc);
                                _beginthread(threadfunc, 0, values+i);
                }
                   for  (; round<max_round; ++round)
                {
                                cout <<  "Main thread is waiting..."   << endl;
                                   // wait for all writer thread are ready
                                WaitForMultipleObjects(thread_count, hRoundDone, TRUE, INFINITE);
                                   for  ( int   i=0; i<thread_count; ++i)
                                {
                                                SetEvent(hGo[i]);
                                }
                                cout <<  "round="  << round << endl;
                }
                   for  ( int   i=0; i<4; ++i)
                {
                                CloseHandle(hGo[i]);
                                CloseHandle(hRoundDone[i]);
                }
                   return  0;
}

试验:即使把threadfunc改成下面的代码,即让SetEvent和 WaitForSingleObject有个不确定的间隔,跑了二十多下,没有死锁, 结果也正确。
#include   <time.h>
void  threadfunc( void   * pi)
{
                srand( (   unsigned  )time( NULL ) );
                   int  i = *(( int   *)pi);
                   int  fileId = (i+3)%thread_count;
                   while  (round < max_round)
                {
                                SetEvent(hRoundDone[i-1]);
                                Sleep(rand()%100);
                                WaitForSingleObject(hGo[i-1], INFINITE);
                                files[fileId] << i <<  " "  ;
                                fileId = (fileId+3)%thread_count;
                }
}
 
跨平台的实现:
#include <iostream>
#include <fstream>
#include <thread>
#include <mutex>
#include <condition_variable>

const int thread_count = 4;
bool go[thread_count] = { false, false, false, false };
std::mutex mtx;
std::condition_variable cv;
std::ofstream files[thread_count];

const int max_round = 10;

void threadfunc(int i)
{
    int fileId = i;
    for (int this_round = 0; this_round<max_round; ++this_round)
    {
        {
            std::unique_lock<std::mutex> lck(mtx);
            while (!go[i - 1])
                cv.wait(lck);
            go[i - 1] = false;
        }
        fileId = (fileId + 3) % thread_count;
        files[fileId] << i << " ";
    }
}

int main()
{
    const char* filename[thread_count] = { "A.txt", "B.txt" , "C.txt" , "D.txt" };
    std::thread threads[thread_count];
    for (int i = 0; i<thread_count; ++i)
    {

        files[i].open(filename[i], std::ios_base::trunc);
        threads[i] = std::thread(threadfunc, i + 1);
    }
    for (int this_round = 0; this_round<max_round; ++this_round)
    {
        std::cout << "Main thread is waiting..." << std::endl;
        // wait for all writer thread are ready
        while (go[0] || go[1] || go[2] || go[3]) {
            std::this_thread::yield();
        }
        {
            std::unique_lock<std::mutex> lck(mtx);
            go[0] = true;
            go[1] = true;
            go[2] = true;
            go[3] = true;
            cv.notify_all();
        }
        std::cout << "this_round=" << this_round << std::endl;
    }

    for (int i = 0; i<thread_count; ++i)
    {
        threads[i].join();
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值