求实现c#多线程数据同步读写代码或思路

本人是c#新手,现在需要实现一个算例,总共4个参数,a,b,box1,box2,分为两个线程线程1和线程2,a,b是线程1和2里的参数,box1和box2可以理解为生产者消费者问题中的缓冲区。具体地,线程1先从box2读给a,a经过一些变化话,再赋给box1;对应地,线程2先从box1读给b,b经过一些变化话,再赋给box2;要求两线程在每一个循环过程中,同步读和写(例如,如果线程1中a读完了但线程2中b还没完成,就等待,直到全部读入工作完成后各线程再继续接下来的代码,在写的过程中也是一样,保证一个循环内两线程全部完成写工作再跳到下一个循环)上述问题可以理解为一个线程互相依赖的生产者消费者问题,即一个线程又是生产者 又是消费者

线程1

for(i=0;i<3;i++)
{
a=box2;//a读box2
。。。//等待线程2完成
a++;
box1=a;//a写给box1
。。。//等待线程2完成
}

线程2

for(i=0;i<3;i++)
{
b=box1;//b读box1
。。。//等待线程1完成
b++;
box2=b;//b写给box2
。。。//等待线程1完成
}

这里我们不扯设计模式的问题,先来从现实角度想象一下这个场景。


假定原本厂里的工人是这样干活的:他只知道“从某厂房A(输入)拿一个零件,加工完毕后送入另一厂房B(输出)。我们只需要告诉他“开始干活(调用了BeginWork方法)”然后他就会开始循环的干活直到你告诉他停下来(EndWork)。当然了,他只用遵守唯一的一条规矩:A厂房里有零件他就拿出来加工,没有零件自然就是等着了。


你可以注意到这个工人自身只关心几件事物:厂房source,厂房target,(被命令)开始循环工作,(被命令)结束工作。换做程序语言,他的实现大体是这样的:

public class SimpleWorker
{
    public SimpleWorker(Factory source,Factory target){...}
    public async Task BeginWork(){while(...){...}}
    public async Task EndWork(){...}
}

(注意两个异步方法。不使用同步方法的原因是活儿是工人干的,老板只是叫他“开始工作”和“停止工作”而已。实际上在生产-消费设计模式中也并不建议由主线程或调度线程自己开子线程来运行各个工作线程。)


不久后我们有了新的工作要求,就是让工人们同步的进行某项操作。想象一下我们有一个工长,他负责管理手下的几个工人,要求他们取零件加工时一起做,当大家都完成后,工长会命令大家一起把加工好的零件放到各自的输出厂房去,完成后再次叫大家一起取零件加工,如此往复。


那么如何站在编程的角度实现呢?


首先我们注意到工人关注的事情变成了:厂房A,厂房B,工长叫他取零件加工,工长叫他送加工后的零件到目的厂房。这中间还涉及一个事儿,因为加工完零件并不一定会立刻送到目的厂房,所以工人还需要找地方把刚加工好的零件存起来。其次,我们多了一个工长,他将负责统一调度所有工人干活,当然了,对于当老板的我们来说,工长只需要接受老板的“开始工作”和“停止工作”命令。


于是我们有了这样的工人:

public class Worker
{
    private Product processedProduct;
    public Worker(Factory source,Factory target){...}
    public async Task GetAndProcess(){...}
    public async Task Put(){...}
}


请注意工人加工零件的方法和送零件的方法被我们从原来的BeginWork中抽取了出来以方便工长做调度。


下来是我们的工长:

public class WorkerManager
{
    private ICollection<Worker> myWorkers;
    private bool requiredToStop;
    private TaskCompletionSource<bool> allWorksDone=new TaskCompletionSource<bool>();
    public WorkerManager(ICollection<Worker> workers)
    {
        myWorkers=workers;
    }
    public async Task BeginWork()
    {
        await Task.Factory.StartNew(()=>
        {
            var taskList=new List<Task>();
            while(!requiredToStop)
            {
                //叫所有工人一起取零件加工(并行)
                foreach(var worker in myWorkers)
                {
                    taskList.Add(worker.GetAndProcess());
                }
                //等到所有工人都完成
                await Task.WhenAll(taskList.ToArray());
                taskList.Clear();
                //叫所有工人一起送零件到各自的目的厂房(并行)
                foreach(var worker in myWorkers)
                {
                    taskList.Add(worker.Put());
                }
                //等到所有工人都完成
                await Task.WhenAll(taskList.ToArray());
                taskList.Clear();
            }
            allWorksDone.SetResult(true);
        });
    }
    public async Task EndWork()
    {
        //不再执行下一轮任务
        requiredToStop=true;
        //一直等到所有当前任务执行完毕
        await allWorksDone.Task;
    }
}


如此一来,作为boss,我们只需要:

var boxA=new Factory();

var boxB=new Factory();

//... 放点东西在两个仓库

var worker1=new Worker(boxA,boxB);

var worker2=new Worker(boxB,boxA);

var workerManager=new WorkerManager(new[]{worker1,worker2});

workerManager.BeginWork();

await workerManager.EndWork();


代码是手写的,省略了一些简单的东西,请自行补全。


最后,关于“停止工作”请注意一点,为了方便举例,我特意将所有的BeginWork和EndWork方法都写成了异步。这两个方法的异步返回时间都是在while循环跳出后,功能上有所重叠。实际上我们只需要其中任意一个方法为异步即可满足所有需求。


有疑问请追问。

温馨提示:内容为网友见解,仅供参考
无其他回答
相似回答