首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >多线程性能提升

多线程性能提升
EN

Stack Overflow用户
提问于 2011-09-16 04:13:29
回答 2查看 901关注 0票数 1

谁能告诉我为什么这些DoCalculation方法中的一个比另一个快得多(比如快40% )?

我有等待设置ManualResetEvents的主线程:

代码语言:javascript
复制
private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
{
ThreadPool.QueueUserWorkItem((obj) =>
{
    ManualResetEvent[] finishcalc = new ManualResetEvent[] 
    { 
        new ManualResetEvent(false), 
        new ManualResetEvent(false), 
        new ManualResetEvent(false), 
        new ManualResetEvent(false), 
        new ManualResetEvent(false), 
        new ManualResetEvent(false) 
    };
    TimeSpan time1 = new TimeSpan(DateTime.Now.Ticks);
    DoCalculation(rand.Next(10), rand.Next(10), 1, finishcalc[0]);
    DoCalculation(rand.Next(10), rand.Next(10), 2, finishcalc[1]);
    DoCalculation(rand.Next(10), rand.Next(10), 3, finishcalc[2]);
    DoCalculation(rand.Next(10), rand.Next(10), 4, finishcalc[3]);
    DoCalculation(rand.Next(10), rand.Next(10), 5, finishcalc[4]);
    DoCalculation(rand.Next(10), rand.Next(10), 6, finishcalc[5]);

    if (WaitHandle.WaitAll(finishcalc))
    {            
        TimeSpan time2 =new TimeSpan(DateTime.Now.Ticks);
        AddTextAsync(string.Format("DoCalculation Finish in {0}\n" ,(time2-time1).TotalSeconds));
    }
});
}

然后我有一个方法,创建另一个线程来按顺序执行一些计算,这就是,我需要上一个线程的结果来继续下面的计算。我找到了两种方法,这是针对Silverlight的。

在第一个示例中,我创建了一个新线程,它等待每次连续的计算完成后再继续:

代码语言:javascript
复制
void DoCalculation(int number1, int number2, int callid, ManualResetEvent calcdone)
{
    ThreadPool.QueueUserWorkItem((obj0) =>
    {
        AddTextAsync(string.Format("The values for Callid {0} are {1} and {2}\n", callid, number1, number2));
        int result = 0;
        ManualResetEvent mresetevent = new ManualResetEvent(false);
        ThreadPool.QueueUserWorkItem((obj) =>
        {
            result = number1 + number2;
            mresetevent.Set();
        });
        mresetevent.WaitOne();
        mresetevent.Reset();
        ThreadPool.QueueUserWorkItem((obj2) =>
        {
            result *= result;
            mresetevent.Set();
        });
        mresetevent.WaitOne();
        mresetevent.Reset();

        ThreadPool.QueueUserWorkItem((obj2) =>
        {
            result *= 2;
            mresetevent.Set();
        });
        mresetevent.WaitOne();
        AddTextAsync(string.Format("The result for Callid {0} is {1} \n", callid, result));
        calcdone.Set();
    });
}

第二个DoCalculation示例我使用一个类作为链接,将一个操作作为参数传递给ThreadPool,并使用它作为回调来创建链中的第二个和第三个线程:

链接类:

代码语言:javascript
复制
public class CalcParams
{
    public int CallID;
    public ManualResetEvent ManualReset;
    public int Result;
    public Action<int, ManualResetEvent, int> CallbackDone;
}

异步服务示例:

代码语言:javascript
复制
public static void DownloadDataInBackground(CalcParams calcparams)
{
    WebClient client = new WebClient();
    Uri uri = new Uri("http://www.google.com");
    client.DownloadStringCompleted += (s, e) =>
    {
        CalcParams localparams = (CalcParams)e.UserState;
        localparams.CallbackDone(e.Result.Length + localparams.Result, localparams.ManualReset, localparams.CallID);
    };
    client.DownloadStringAsync(uri, calcparams);
}

和改进的doCalculation方法:

代码语言:javascript
复制
void DoCalculation(int number1, int number2, int callid, ManualResetEvent calcdone)
{
    ThreadPool.QueueUserWorkItem((obj0) =>
    {
        int result = number1+number2;
        doCalculationService.DownloadDataInBackground(new CalcParams()
        {
            Result = result,
            ManualReset = calcdone,
            CallID = callid,
            CallbackDone = (r, m, i) =>
            {
                int sqrt = r * r;
                doCalculationService.DownloadDataInBackground(new CalcParams()
                {
                    Result = sqrt,
                    CallID = i,
                    ManualReset = m,
                    CallbackDone = (r2, m2, i2) =>
                    {
                        int result2 = r2 * 2;
                        AddTextAsync(string.Format("The result for Callid {0} is {1} \n", i2, result2));
                        m2.Set();
                    }
                });
            }
        });
    });
}

谢谢。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2011-09-20 08:59:52

我可以建议您将反应式扩展(Rx)视为在Silverlight中使用多线程的另一种方式吗?

下面是用Rx编写的代码:

代码语言:javascript
复制
Func<int, int, int> calculation = (n1, n2) =>
{
    var r = n1 + n2;
    r *= r;
    r *= 2;
    return r;
};

var query =
    from callid in Observable.Range(0, 6, Scheduler.ThreadPool)
    let n1 = rand.Next(10)
    let n2 = rand.Next(10)
    from result in Observable.Start(() => calculation(n1, n2))
    select new { callid, n1, n2, result };

query.Subscribe(x => { /* do something with result */ });

它会自动将计算推送到线程池中-我将Scheduler.ThreadPool参数放入其中,但这是SelectMany查询的默认设置。

使用这种代码,您通常不需要担心所有的more,并且可以非常容易地阅读代码,从而可以更容易地进行测试。

Rx是受支持的Microsoft产品,可在桌面CLR和Silverlight上运行。

以下是Rx的链接:

  • Reactive Extensions Forum
  • Reactive Extensions (Rx) v1.0.10621

哦,我认为你会得到非常不同的性能结果的原因是Silverlight在计时方面只有毫秒的分辨率,所以你真的必须运行数千次计算才能得到一个好的平均值。

EDIT:根据注释中的请求,下面是使用Rx链接每个中间计算的结果的示例。

代码语言:javascript
复制
Func<int, int, int> fn1 = (n1, n2) => n1 + n2;
Func<int, int> fn2 = n => n * n;
Func<int, int> fn3 = n => 2 * n;

var query =
    from callid in Observable.Range(0, 6, Scheduler.ThreadPool)
    let n1 = rand.Next(10)
    let n2 = rand.Next(10)
    from r1 in Observable.Start(() => fn1(n1, n2))
    from r2 in Observable.Start(() => fn2(r1))
    from r3 in Observable.Start(() => fn3(r2))
    select new { callid, n1, n2, r1, r2, r3 };

当然,这三个lambda函数也可以很容易地成为常规方法函数。

另一种选择是,如果您有使用BeginInvoke/EndInvoke异步模式的函数,则可以像这样使用FromAsyncPattern扩展方法:

代码语言:javascript
复制
Func<int, int, IObservable<int>> ofn1 =
    Observable.FromAsyncPattern<int, int, int>
        (fn1.BeginInvoke, fn1.EndInvoke);

Func<int, IObservable<int>> ofn2 =
    Observable.FromAsyncPattern<int, int>
        (fn2.BeginInvoke, fn2.EndInvoke);

Func<int, IObservable<int>> ofn3 =
    Observable.FromAsyncPattern<int, int>
        (fn3.BeginInvoke, fn3.EndInvoke);

var query =
    from callid in Observable.Range(0, 6, Scheduler.ThreadPool)
    let n1 = rand.Next(10)
    let n2 = rand.Next(10)
    from r1 in ofn1(n1, n2)
    from r2 in ofn2(r1)
    from r3 in ofn3(r2)
    select new { callid, n1, n2, r1, r2, r3 };

前面有点混乱,但查询稍微简单了一点。

注意:同样,Scheduler.ThreadPool参数也是不必要的,但它只是为了显式地显示查询是使用线程池执行的。

票数 2
EN

Stack Overflow用户

发布于 2011-09-16 06:11:48

没有充分的理由调用ThreadPool.QueueUserWorkItem,然后立即等待它完成。也就是说,编写以下代码:

代码语言:javascript
复制
ThreadPool.QueueUserWorkItem(() =>
    {
        // do stuff
        mevent.Set();
    });
mevent.WaitOne();

不会给你带来任何好处。你的主线程会等待结束。事实上,这比仅仅写:

代码语言:javascript
复制
// do stuff

因为线程池必须启动一个线程。

您可以通过删除所有嵌套的“异步”工作来简化和加速您的第一个DoCalculation方法:

代码语言:javascript
复制
void DoCalculation(int number1, int number2, int callid, ManualResetEvent calcdone)
{
    ThreadPool.QueueUserWorkItem((obj0) =>
    {
        AddTextAsync(string.Format("The values for Callid {0} are {1} and {2}\n", callid, number1, number2));
        int result = 0;

        result = number1 + number2;
        result *= result;
        result *= 2;

        AddTextAsync(string.Format("The result for Callid {0} is {1} \n", callid, result));
        calcdone.Set();
    });
}

针对更新的问题进行编辑

你的新例子#3在一定程度上简化了事情,但仍然没有抓住要点。下面是执行新的DoCalculation方法时会发生的情况:

  1. ThreadQueue.QueueUserWorkItem创建一个新线程,DoCalculation方法退出。现在,您有一个后台线程正在运行。我们将其称为Thread1。
  2. 代码调用DownloadDataInBackground。该方法启动另一个线程来异步下载数据。调用该线程2。
  3. 线程1退出。
  4. 当线程2完成下载时,它调用完成回调,该回调再次调用DownloadDataInBackground。这将创建线程3,开始执行,然后线程2退出。
  5. 当线程3完成下载时,它会调用完成回调,执行计算,输出一些数据,然后退出。

所以你启动了三个线程。在任何时候,都没有任何有意义的“多线程”在进行。也就是说,任何时候都没有超过一个线程在做有意义的工作。

您的任务是按顺序执行的,因此没有理由启动多个线程才能运行它们。

如果您只需编写以下代码,您的代码将更干净,执行速度也会更快(因为不必启动太多线程):

代码语言:javascript
复制
ThreadPool.QueueUserWorkItem((obj0) =>
{
    DownloadString(...); // NOT DownloadStringAsync
    DownloadString(...);
    // Do calculation
});

一个线程按顺序执行每个任务。

只有当您希望同时执行多个任务时,才需要多个线程。很明显,这不是你要做的。事实上,你的问题是:

然后我有一个方法,它创建另一个线程来按顺序进行一些计算,这就是说,我需要上一个线程的结果来继续下一个线程。,

顺序任务意味着一个线程。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7436915

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档