论坛首页 编程语言技术论坛

多线程是个不靠谱的东西

浏览 32954 次
该帖已经被评为精华帖
作者 正文
   发表时间:2008-05-06  
这几天搞Parllel,才发现多线程远比想像中的困难,而不只是资源的冲突和锁定那么简单。利用多线程,首要目标是让任务并行,让数据的处理更有效率。但是,问题是什么样子的数据该并行处理??

首先我排除了大部分的文件读写操作,顺序读取会导致文件指针的移动,这显然会导致不确定数据结果,特别是要把数据填充到一系列结构(Struct)中去的时候,多线程产生了一堆错误的结果。

其次内存中数据操作似乎也不好确定是否该用多线程,线程的生产、切换、消费都要消耗时间,在single core的机器上,多线程只会更没效率而不是更有效率。对于multi core的机器呢。。。情况似乎变得复杂,线程时间片和操作时间片的长度似乎是一个很关键的因素。对于“短”操作来说,花在线程消费上的开销,远远大于起并行带来的时间减少,因此,多线程只会更慢,而不是更快。但难点是,如何找到一个临界点,以作为是否引入多线程操作的阀值? 线程消费的开销,可能随着机器的情况而变化,包括CPU的usage,CPU的核心数目。。。。


我翻了MC#(multi core C#),PLinq,Parallel C#,谁也没告诉我答案,我们不缺工具,但缺best practise
   发表时间:2008-05-06  
Parallel.For的实现
public delegate void ForDelegate(int i);
public delegate void ThreadDelegate();
public class Parallel
{

/// <summary>
/// Parallel for loop. Invokes given action, passing arguments 
/// fromInclusive - toExclusive on multiple threads.
/// Returns when loop finished.
/// </summary>

public static void For(int fromInclusive, int toExclusive, ForDelegate action)
{

    // ChunkSize = 1 makes items to be processed in order.
    // Bigger chunk size should reduce lock waiting time and thus
    // increase paralelism.
    int chunkSize = 4;
    // number of process() threads
    int threadCount = Environment.ProcessorCount;
    int cnt = fromInclusive - chunkSize;
    // processing function
    // takes next chunk and processes it using action
    ThreadDelegate process = delegate()
    {

        while (true)
        {

            int cntMem = 0;
            lock (typeof(Parallel))
            {
                // take next chunk
                cnt += chunkSize;
                cntMem = cnt;
            }

            // process chunk
            // here items can come out of order if chunkSize > 1
            for (int i = cntMem;  i < cntMem + chunkSize; ++i)
            {
                if (i >= toExclusive) return;
                action(i);
            }
        }
    };

    // launch process() threads
    IAsyncResult[] asyncResults = new IAsyncResult[threadCount];
    for (int i = 0; i < threadCount; ++i)
    {
        asyncResults[i] = process.BeginInvoke(null, null);
    }

    // wait for all threads to complete
    for (int i = 0; i < threadCount; ++i)
    {
        process.EndInvoke(asyncResults[i]);
    }
}

0 请登录后投票
   发表时间:2008-05-06  
从开发应用程序的角度来看 是否需要多线程跟应用环境有关 跟核心的多少无关
多线程的瓶颈在于切换上下文和锁

>>首先我排除了大部分的文件读写操作,顺序读取会导致文件指针的移动
>>特别是要把数据填充到一系列结构(Struct)中去的时候,多线程产生了一堆错误的结果
如果这种错误你都要犯,我只能说两点:
1 你不知道怎么合理使用锁
2 你不知道文件系统是怎么运作的
有空看看filesystem的科普文章吧,或者看看linux是怎么维护filenode的

而且,你这帖子属于文不对题,内容分明是在说:我不会用多线程
0 请登录后投票
   发表时间:2008-05-06  
seen 写道
从开发应用程序的角度来看 是否需要多线程跟应用环境有关 跟核心的多少无关
多线程的瓶颈在于切换上下文和锁

>>首先我排除了大部分的文件读写操作,顺序读取会导致文件指针的移动
>>特别是要把数据填充到一系列结构(Struct)中去的时候,多线程产生了一堆错误的结果
如果这种错误你都要犯,我只能说两点:
1 你不知道怎么合理使用锁
2 你不知道文件系统是怎么运作的
有空看看filesystem的科普文章吧,或者看看linux是怎么维护filenode的

而且,你这帖子属于文不对题,内容分明是在说:我不会用多线程

事实上,他说的问题极端重要。如果一种并行设施无法平滑的从顺序代码进行迁移,那么就没有什么太大的价值.你从顺序型的代码写好一个程序,当你要把它们并行化的时候居然发现要从新推倒从来,那几乎是不可想象的.因为直接针对并行开发应用的代价是非常昂贵的。当你在编写业务逻辑的时候提前考虑并行问题无疑就是引入复杂性,这就如C/C++里一边写业务逻辑一边要考虑内存和指针,是一种极大的负担.但是当你完成业务逻辑以后,通过并行来提升效率就提升到了第一位,但是这个时候如果再把业务牵扯进来,又会引发极大的混乱。

引用
首先我排除了大部分的文件读写操作,顺序读取会导致文件指针的移动,这显然会导致不确定数据结果,特别是要把数据填充到一系列结构(Struct)中去的时候,多线程产生了一堆错误的结果。

像类似这种问题,如果不从语言设施特别是类型系统上进行根本的改观,只是依靠加入特定的并行补丁毫无疑问是一种极大的灾难.最生动的例子,想想C++里面再不引入GC的前提下引入exception,这种脑残设计的会导致什么后果.
0 请登录后投票
   发表时间:2008-05-06  
seen 写道
从开发应用程序的角度来看 是否需要多线程跟应用环境有关 跟核心的多少无关
多线程的瓶颈在于切换上下文和锁

>>首先我排除了大部分的文件读写操作,顺序读取会导致文件指针的移动
>>特别是要把数据填充到一系列结构(Struct)中去的时候,多线程产生了一堆错误的结果
如果这种错误你都要犯,我只能说两点:
1 你不知道怎么合理使用锁
2 你不知道文件系统是怎么运作的
有空看看filesystem的科普文章吧,或者看看linux是怎么维护filenode的

而且,你这帖子属于文不对题,内容分明是在说:我不会用多线程


你不过是在犯傻而已. 线程是什么,CPU的时间片而已. 对于单核或者单CPU来说,线程是什么? CPU时间的轮换而已,CPU时间片的轮换必然带来开销,所以对于单核来说同样Print out一个List里的data, 多线程的速度会远比单线程慢,而多核系统,好处是两个线程可以被安排到两个核心上,才有可能加速处理玩List里的Data,(如果不是Print out这样简单的东西,而是FF变换).


第二,你压根国文就不及格.我说的是读取一系列Struct结构,文件的构成是包含的是:

(Struct A)(Struct B)(Struct B)(Struct C).....

无法使用多线程是因为Struct B的文件读取位置,受到Struct A Size的限制.

你这样傻,我为什么还要和你解释呢?
0 请登录后投票
   发表时间:2008-05-06  
Trustno1 写道
像类似这种问题,如果不从语言设施特别是类型系统上进行根本的改观,只是依靠加入特定的并行补丁毫无疑问是一种极大的灾难.最生动的例子,想想C++里面再不引入GC的前提下引入exception,这种脑残设计的会导致什么后果.


如何将顺序结构切换成并行结构是一个难点...而什么时候该实施结构转换,是难点的难点.

我利用Parallel处理Assmebly结构,单核上要比顺序结构慢20%,双核机器上比顺利结构快5%...只能说,并行得还不够啊!!!
0 请登录后投票
   发表时间:2008-05-06  
WALL.E 写道
引用

事实上,他说的问题极端重要。如果一种并行设施无法平滑的从顺序代码进行迁移,那么就没有什么太大的价值.你从顺序型的代码写好一个程序,当你要把它们并行化的时候居然发现要从新推倒从来,那几乎是不可想象的.因为直接针对并行开发应用的代价是非常昂贵的。当你在编写业务逻辑的时候提前考虑并行问题无疑就是引入复杂性,这就如C/C++里一边写业务逻辑一边要考虑内存和指针,是一种极大的负担.但是当你完成业务逻辑以后,通过并行来提升效率就提升到了第一位,但是这个时候如果再把业务牵扯进来,又会引发极大的混乱。


说这话没有理由。顺序代码凭什么应该顺利地迁移到并行代码?除非编写的时候就为并行优化,顺序代码一般都严重依赖上下文关系。打破这个关系自然跟重写没什么区别。

传闻IBM花了大量时间研制并行编译器。如果顺序代码都可以顺利地转化成并行代码,那这个东西早就搞出来了。而且PS3也不会弄得现在这样子。

话是不能说的那么绝对滴.....


引用
如何将顺序结构切换成并行结构是一个难点...而什么时候该实施结构转换,是难点的难点.

我利用Parallel处理Assmebly结构,单核上要比顺序结构慢20%,双核机器上比顺利结构快5%...只能说,并行得还不够啊!!!

俗话说,只有想不到,没有做不到.
赫赫.
0 请登录后投票
   发表时间:2008-05-06  
WALL.E 写道
说这话没有理由。顺序代码凭什么应该顺利地迁移到并行代码?除非编写的时候就为并行优化,顺序代码一般都严重依赖上下文关系。打破这个关系自然跟重写没什么区别。

传闻IBM花了大量时间研制并行编译器。如果顺序代码都可以顺利地转化成并行代码,那这个东西早就搞出来了。而且PS3也不会弄得现在这样子。


就是因为我们编程能力已经落后于硬件的进步了.大部分时候,我们都是用顺序的方式去思考逻辑. 我的想法并不是要完全打破顺序结构,而是在顺序结构适当中引入并行结构.

比如从文件中读数据--->对每个数据进行傅立叶变换--->把结果保存到文件中.

就可以用顺序--->并行--->顺序的方式来执行.

我想从现在到未来,CPU的数目只会更多, 昨天送来的样机有2个4核CPU 加 两张 2个双核 GPU, 12个core让你使用,并行只会更重要.....
0 请登录后投票
   发表时间:2008-05-06  
Trustno1 写道
seen 写道
从开发应用程序的角度来看 是否需要多线程跟应用环境有关 跟核心的多少无关
多线程的瓶颈在于切换上下文和锁

>>首先我排除了大部分的文件读写操作,顺序读取会导致文件指针的移动
>>特别是要把数据填充到一系列结构(Struct)中去的时候,多线程产生了一堆错误的结果
如果这种错误你都要犯,我只能说两点:
1 你不知道怎么合理使用锁
2 你不知道文件系统是怎么运作的
有空看看filesystem的科普文章吧,或者看看linux是怎么维护filenode的

而且,你这帖子属于文不对题,内容分明是在说:我不会用多线程

事实上,他说的问题极端重要。如果一种并行设施无法平滑的从顺序代码进行迁移,那么就没有什么太大的价值.你从顺序型的代码写好一个程序,当你要把它们并行化的时候居然发现要从新推倒从来,那几乎是不可想象的.因为直接针对并行开发应用的代价是非常昂贵的。当你在编写业务逻辑的时候提前考虑并行问题无疑就是引入复杂性,这就如C/C++里一边写业务逻辑一边要考虑内存和指针,是一种极大的负担.但是当你完成业务逻辑以后,通过并行来提升效率就提升到了第一位,但是这个时候如果再把业务牵扯进来,又会引发极大的混乱。

引用
首先我排除了大部分的文件读写操作,顺序读取会导致文件指针的移动,这显然会导致不确定数据结果,特别是要把数据填充到一系列结构(Struct)中去的时候,多线程产生了一堆错误的结果。

像类似这种问题,如果不从语言设施特别是类型系统上进行根本的改观,只是依靠加入特定的并行补丁毫无疑问是一种极大的灾难.最生动的例子,想想C++里面再不引入GC的前提下引入exception,这种脑残设计的会导致什么后果.


我觉得你想说的其实是:除非语言设施支持并发,不然并发编程是很容易引起混乱的,是代价昂贵的。
假设我猜对了,那我的感觉是,这话听起来很像另外一个常见的看法:除非语言设施屏蔽指针,不然内存操作是很容易引起混乱的,是代价昂贵的。
话说回来,并发的难度比指针的难度要高多了。但世界上用c写的并发的应用并不都是失败的。
0 请登录后投票
   发表时间:2008-05-06  
引用
话说回来,并发的难度比指针的难度要高多了。但世界上用c写的并发的应用并不都是失败的。

这要注意上下文的语境了.
成功!=开发低成本.
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics