Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/334.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 我可以更好地优化这种并发性吗?_C#_.net_Multithreading_Concurrency - Fatal编程技术网

C# 我可以更好地优化这种并发性吗?

C# 我可以更好地优化这种并发性吗?,c#,.net,multithreading,concurrency,C#,.net,Multithreading,Concurrency,我最近开始编写我的第一个多线程代码,希望您能给我一些建议 它从由流解析器在后台填充的缓冲区中传递视频样本(不在本问题的范围内)。如果缓冲区为空,则需要等待缓冲区级别变为可接受,然后继续 代码用于Silverlight 4,删除了一些错误检查: // External class requests samples - can happen multiple times concurrently protected override void GetSampleAsync() { Inter

我最近开始编写我的第一个多线程代码,希望您能给我一些建议

它从由流解析器在后台填充的缓冲区中传递视频样本(不在本问题的范围内)。如果缓冲区为空,则需要等待缓冲区级别变为可接受,然后继续

代码用于Silverlight 4,删除了一些错误检查:

// External class requests samples - can happen multiple times concurrently
protected override void GetSampleAsync()
{
    Interlocked.Add(ref getVideoSampleRequestsOutstanding, 1);                
}

// Runs on a background thread
void DoVideoPumping()
{
    do
    {
        if (getVideoSampleRequestsOutstanding > 0)
        {
            PumpNextVideoSample();

            // Decrement the counter
            Interlocked.Add(ref getVideoSampleRequestsOutstanding, -1);
        }
        else Thread.Sleep(0);  

    } while (!this.StopAllBackgroundThreads);
}       

void PumpNextVideoSample()
{
    // If the video sample buffer is empty, tell stream parser to give us more samples 
    bool MyVidBufferIsEmpty = false; bool hlsClientIsExhausted = false;
    ParseMoreSamplesIfMyVideoBufferIsLow(ref MyVidBufferIsEmpty, ref parserAtEndOfStream);

    if (parserAtEndOfStream)  // No more data, start running down buffers
        this.RunningDownVideoBuffer = true;
    else if (MyVidBufferIsEmpty)  
    {
        // Buffer is empty, wait for samples
        WaitingOnEmptyVideoBuffer = true;
        WaitOnEmptyVideoBuffer.WaitOne();
    }

    // Buffer is OK
    nextSample = DeQueueVideoSample(); // thread-safe, returns NULL if a problem

    // Send the sample to the external renderer
    ReportGetSampleCompleted(nextSample);

}
代码似乎运行良好。然而,我被告知使用Thread.Wait(…)是“邪恶的”:当没有请求样本时,我的代码会不必要地循环,占用CPU时间

我的代码可以进一步优化吗?既然我的类是为一个需要样本的环境设计的,那么潜在的“无意义循环”场景是否会超过当前设计的简单性呢


非常感谢您的评论。

这看起来像是经典的生产者/消费者模式。解决这个问题的通常方法是使用所谓的阻塞队列

net的4.0版为这类问题引入了一套高效、设计良好的解决方案。我想它能满足你现在的需要

如果您无法访问.NET4.0,那么许多网站都包含阻塞队列的实现。就我个人而言,我的标准参考书是乔·达菲的书。这将是一个良好的开端

使用阻塞队列的第一个优点是,您不再使用繁忙的等待循环、对
Sleep()
等的恶意调用。使用阻塞队列来避免此类代码始终是一个好主意


然而,我认为使用阻塞队列有一个更重要的好处。目前,用于生成工作项、使用它们和处理队列的代码都是混杂的。如果您正确使用阻塞队列,那么您将得到更好的分解代码,它将保留算法的各个组件:队列、生产者和消费者。

这看起来像经典的生产者/消费者模式。解决这个问题的通常方法是使用所谓的阻塞队列

net的4.0版为这类问题引入了一套高效、设计良好的解决方案。我想它能满足你现在的需要

如果您无法访问.NET4.0,那么许多网站都包含阻塞队列的实现。就我个人而言,我的标准参考书是乔·达菲的书。这将是一个良好的开端

使用阻塞队列的第一个优点是,您不再使用繁忙的等待循环、对
Sleep()
等的恶意调用。使用阻塞队列来避免此类代码始终是一个好主意


然而,我认为使用阻塞队列有一个更重要的好处。目前,用于生成工作项、使用它们和处理队列的代码都是混杂的。如果您正确使用阻塞队列,那么您将得到更好的分解代码,它将保留算法的不同组件:队列、生产者和消费者。

您有一个主要问题:
Thread.Sleep()

它的粒度约为20毫秒,这对于视频来说有点粗糙。此外,
Sleep(0)
可能存在低优先级线程不足的问题[]


更好的方法是等待Waithandle,最好内置在队列中

您有一个主要问题:
Thread.Sleep()

它的粒度约为20毫秒,这对于视频来说有点粗糙。此外,
Sleep(0)
可能存在低优先级线程不足的问题[]

更好的方法是等待Waithandle,最好内置在队列中

是阻塞队列的一个简单好例子。
主键是线程需要与信号协调,而不是通过检查计数器的值或数据结构的状态。任何检查都需要资源(CPU),因此您需要信号(Monitor.Wait和Monitor.Pulse)。

是阻塞队列的一个简单好例子。
主键是线程需要与信号协调,而不是通过检查计数器的值或数据结构的状态。任何检查都需要资源(CPU),因此您需要信号(Monitor.Wait和Monitor.Pulse)。

您可以使用线程而不是手动线程.sleep。这样做相当简单:

AutoResetEvent e;
void RequestSample()
{
    Interlocked.Increment(ref requestsOutstanding);
    e.Set(); //also set this when StopAllBackgroundThreads=true!
}

void Pump()
{
    while (!this.StopAllBackgroundThreads) {
        e.WaitOne();
        int leftOver = Interlocked.Decrement(ref requestsOutstanding);
        while(leftOver >= 0) {
            PumpNextVideoSample();
            leftOver = Interlocked.Decrement(ref requestsOutstanding);
        }
        Interlocked.Increment(ref requestsOutstanding);
    }
}
注意,实现信号量可能更具吸引力。大体上在您的场景中,同步开销可能几乎为零,而一个更简单的编程模型是值得的。有了信号灯,你会有这样的东西:

MySemaphore sem;
void RequestSample()
{
    sem.Release();
}

void Pump()
{
    while (true) {
        sem.Acquire();
        if(this.StopAllBackgroundThreads) break;
        PumpNextVideoSample();
    }
}
…我认为简单是值得的

e、 g.信号量的简单实现:

public sealed class SimpleSemaphore
{
    readonly object sync = new object();
    public int val;

    public void WaitOne()
    {
        lock(sync) {
            while(true) {
                if(val > 0) {
                    val--;
                    return;
                }
                Monitor.Wait(sync);
            }
        }
    }

    public void Release()
    {
        lock(sync) {
            if(val==int.MaxValue)
                throw new Exception("Too many releases without waits.");
            val++;
            Monitor.Pulse(sync);
        }
    }
}
在一个简单的基准上,这个简单的实现需要约1.7秒,其中信号量需要7.5秒,信号量lim需要1.1秒;换句话说,非常合理。

您可以使用线程而不是手动线程。睡眠。这样做相当简单:

AutoResetEvent e;
void RequestSample()
{
    Interlocked.Increment(ref requestsOutstanding);
    e.Set(); //also set this when StopAllBackgroundThreads=true!
}

void Pump()
{
    while (!this.StopAllBackgroundThreads) {
        e.WaitOne();
        int leftOver = Interlocked.Decrement(ref requestsOutstanding);
        while(leftOver >= 0) {
            PumpNextVideoSample();
            leftOver = Interlocked.Decrement(ref requestsOutstanding);
        }
        Interlocked.Increment(ref requestsOutstanding);
    }
}
注意,实现信号量可能更具吸引力。大体上在您的场景中,同步开销可能几乎为零,而一个更简单的编程模型是值得的。有了信号灯,你会有这样的东西:

MySemaphore sem;
void RequestSample()
{
    sem.Release();
}

void Pump()
{
    while (true) {
        sem.Acquire();
        if(this.StopAllBackgroundThreads) break;
        PumpNextVideoSample();
    }
}
…我认为简单是值得的

e、 g.信号量的简单实现:

public sealed class SimpleSemaphore
{
    readonly object sync = new object();
    public int val;

    public void WaitOne()
    {
        lock(sync) {
            while(true) {
                if(val > 0) {
                    val--;
                    return;
                }
                Monitor.Wait(sync);
            }
        }
    }

    public void Release()
    {
        lock(sync) {
            if(val==int.MaxValue)
                throw new Exception("Too many releases without waits.");
            val++;
            Monitor.Pulse(sync);
        }
    }
}

在一个简单的基准上,这个简单的实现需要约1.7秒,其中信号量需要7.5秒,信号量lim需要1.1秒;换句话说,非常合理。

Thread.Sleep(0);->睡眠(10);有一个
Interlocked.Increment()
Interlocked.Decrement()
:)@Eugene我把你的评论理解为Thread.Sleep(0)也可以是Thread.Sleep(10),因为它至少会受到惩罚10ms@Carlos永远不要使用Thread.Sleep(0),因为它没用——它会极大地利用你的CPU。设置实际延迟的最佳方法-它将减少CPU消耗,尤其是在do/while情况下。@Eugene,@TimWi:Thread.Sleep(0);->睡眠(10);有