C# 后台工作同步

C# 后台工作同步,c#,multithreading,backgroundworker,C#,Multithreading,Backgroundworker,假设我有一个类,它应该为我生成一些ID(例如GUID)。现在不幸的是,ID的生成是一个有点长的过程,如果我需要100个ID,我会遇到一个严重的减速问题。为了避免这些问题,我保留了一个预生成ID的队列,当这个队列开始耗尽它们时,我使用BackgroundWorker生成新的ID并将它们放入队列中。但是我遇到了一些问题。目前最大的问题是如何确保在ID上的队列完全用完的情况下,主线程等待BackroundWorker生成并将它们放入队列中。这是我目前掌握的密码 public class IdGener

假设我有一个类,它应该为我生成一些ID(例如GUID)。现在不幸的是,ID的生成是一个有点长的过程,如果我需要100个ID,我会遇到一个严重的减速问题。为了避免这些问题,我保留了一个预生成ID的队列,当这个队列开始耗尽它们时,我使用BackgroundWorker生成新的ID并将它们放入队列中。但是我遇到了一些问题。目前最大的问题是如何确保在ID上的队列完全用完的情况下,主线程等待BackroundWorker生成并将它们放入队列中。这是我目前掌握的密码

public class IdGenerator
{
    private Queue<string> mIds = new Queue<string>();
    private BackgroundWorker mWorker = new BackgroundWorker();
    private static EventWaitHandle mWaitHandle = new AutoResetEvent(false);

    public IdGenerator()
    {
        GenerateIds();

        this.mWorker.DoWork += new DoWorkEventHandler(FillQueueWithIds);
    }

    private void GenerateIds()
    {
        List<string> ids = new List<string>();

        for (int i = 0; i < 100; i++ )
        {
            ids.Add(Guid.NewGuid().ToString());
        }

        lock (this.mIds)
        {

            foreach (string id in ids)
            {
                this.mIds.Enqueue(id);
            } 
        }            
    }

    public string GetId()
    {
        string id = string.Empty;

        lock (this.mIds)
        {
            if (this.mIds.Count > 0)
            {
                id = this.mIds.Dequeue();
            }

            if (this.mIds.Count < 100)
            {
                if (!this.mWorker.IsBusy)
                {
                    this.mWorker.RunWorkerAsync();
                }
            }
        }

        if (this.mIds.Count < 1)
        {
            mWaitHandle.WaitOne();
        }

        return id;
    }

    void FillQueueWithIds(object sender, DoWorkEventArgs e)
    {
        GenerateIds();
        mWaitHandle.Set();   
    }
}
公共类IdGenerator
{
专用队列mIds=新队列();
私有BackgroundWorker mWorker=新BackgroundWorker();
私有静态EventWaitHandle mWaitHandle=新的AutoResetEvent(false);
公共IdGenerator()
{
GenerateIds();
this.mWorker.DoWork+=新的DoWorkEventHandler(FillQueueWithIds);
}
私有void GenerateIds()
{
列表ID=新列表();
对于(int i=0;i<100;i++)
{
Add(Guid.NewGuid().ToString());
}
锁(此.mIds)
{
foreach(id中的字符串id)
{
此.mIds.Enqueue(id);
} 
}            
}
公共字符串GetId()
{
string id=string.Empty;
锁(此.mIds)
{
如果(this.mIds.Count>0)
{
id=this.mIds.Dequeue();
}
如果(此.mIds.Count<100)
{
如果(!this.mWorker.IsBusy)
{
this.mWorker.RunWorkerAsync();
}
}
}
如果(此.mIds.Count<1)
{
mWaitHandle.WaitOne();
}
返回id;
}
无效FillQueueWithIds(对象发送方,DoWorkEventArgs e)
{
GenerateIds();
mWaitHandle.Set();
}
}
显然它不能正常工作。似乎我在调用WaitOne和Set方法的适当时机上有问题。有时,即使工作人员已经完成了工作,IsBusy属性也会返回true


编辑:


这是一个WinForm,我必须使用.NET 2.0。您遇到的问题是典型的生产者-消费者问题。看看

一个简单的解释是,您将有两个线程。一个是生产者(GUID生成器),另一个是消费者

您将通过使用信号量来保持这些线程的同步。信号量将负责在队列已满时停止生产者,并在队列为空时停止消费者

Wikipedia的文章对这个过程进行了很好的解释,我打赌你可以在互联网上找到c#中生产者-消费者的基本实现。

你的主代码(大概是WinForms)在某一点调用
mWaitHandle.WaitOne()。此时消息泵被阻塞,Bgw将无法调用其已完成的事件。这意味着IsBusy标志仍然为true:死锁

如果DoWork中的代码引发异常,也会出现类似的问题

编辑:


我认为通过使用线程池线程替换Bgw可以解决大多数问题。还有一个简单的
volatile bool isbusy
标志。

有一些与线程同步相关的错误,请参见下面更改的代码。 对队列应用锁同步时,请注意锁定队列的所有使用。 我已经将GetId方法更改为在没有新ID的情况下探测新ID

public class IdGenerator
{
    private Queue<string> mIds = new Queue<string>();
    private BackgroundWorker mWorker = new BackgroundWorker();
    private static EventWaitHandle mWaitHandle = new AutoResetEvent(false);

    public IdGenerator()
    {
        GenerateIds();

        this.mWorker.DoWork += new DoWorkEventHandler(FillQueueWithIds);
    }

    private void GenerateIds()
    {
        List<string> ids = new List<string>();

        for (int i = 0; i < 100; i++ )
        {
            ids.Add(Guid.NewGuid().ToString());
        }

        lock (this.mIds)
        {

            foreach (string id in ids)
            {
                this.mIds.Enqueue(id);
            } 
        }            
    }

    public string GetId()
    {
        string id = string.Empty;
        //Indicates if we need to wait
        bool needWait = false;

        do
        {
            lock (this.mIds)
             {
                if (this.mIds.Count > 0)
                {
                    id = this.mIds.Dequeue();
                    return id;
                }

                if (this.mIds.Count < 100 && this.mIds.Count > 0)
                {
                    if (!this.mWorker.IsBusy)
                    {
                        this.mWorker.RunWorkerAsync();
                    }
                } 
                else 
                {
                    needWait = true;
                }
            }

            if (needWait)
            {
                mWaitHandle.WaitOne();
                needWait = false;
            }
        } while(true);

        return id;
    }

    void FillQueueWithIds(object sender, DoWorkEventArgs e)
    {
        GenerateIds();
        mWaitHandle.Set();   
    }
}
公共类IdGenerator
{
专用队列mIds=新队列();
私有BackgroundWorker mWorker=新BackgroundWorker();
私有静态EventWaitHandle mWaitHandle=新的AutoResetEvent(false);
公共IdGenerator()
{
GenerateIds();
this.mWorker.DoWork+=新的DoWorkEventHandler(FillQueueWithIds);
}
私有void GenerateIds()
{
列表ID=新列表();
对于(int i=0;i<100;i++)
{
Add(Guid.NewGuid().ToString());
}
锁(此.mIds)
{
foreach(id中的字符串id)
{
此.mIds.Enqueue(id);
} 
}            
}
公共字符串GetId()
{
string id=string.Empty;
//指示是否需要等待
bool needWait=false;
做
{
锁(此.mIds)
{
如果(this.mIds.Count>0)
{
id=this.mIds.Dequeue();
返回id;
}
如果(this.mIds.Count<100&&this.mIds.Count>0)
{
如果(!this.mWorker.IsBusy)
{
this.mWorker.RunWorkerAsync();
}
} 
其他的
{
needWait=true;
}
}
如果(需要等待)
{
mWaitHandle.WaitOne();
needWait=false;
}
}虽然(正确);
返回id;
}
无效FillQueueWithIds(对象发送方,DoWorkEventArgs e)
{
GenerateIds();
mWaitHandle.Set();
}
}

在.NET 4中,您可以使用
BlockingCollection
和更一般的
IProducerConsumerCollection

下面是一个使用它的两个任务的示例,一个添加,另一个获取


好的,这是我最后的解决方案。这一个不使用BackgroundWorker,但它可以工作。感谢Edu,他指出了生产者和消费者的问题。我使用了MSDN提供的示例。

这似乎是一个真实的实体模型,这段代码也会出错吗?是WinForms还是WPF还是?这是一个WinForm。上面的代码是