游戏开发论坛

 找回密码
 立即注册
搜索
查看: 2392|回复: 5

头疼的多线程

[复制链接]

89

主题

822

帖子

847

积分

高级会员

Rank: 4

积分
847
发表于 2007-8-25 16:51:00 | 显示全部楼层 |阅读模式
做网络服务器程序不可能不用到多线程,即使只有2个线程也涉及到同步的问题。

先来看一个线程的问题。很多人说一个线程有什么问题,很多时候数据的多少不是我们所能预知的,不可避免的使用动态数组。

动态数组有2种类型,一种是每次都分配新的空间,使得新的空间能够容纳我们所需要的大小,然后把旧空间的数据复制到新的空间,最后释放旧的空间。这样我们总能获得适合我们所需要的空间大小,这种空间在可编程上是连续的。还有一种实际上并不是数组,而是一种链表,所分配的地址在可编程上就不是连续的,只不过在逻辑应用层上我们给每个不连续的空间增加一个数组一样的索引。那么STL标准模版库为我们提供了很好的帮助,除非有人喜欢自己编写那些重复而枯燥的代码。  

不管是哪种类型,为了减少的存放对象申请释放所带来的开销,我一般不在STL模版里面存放对象实例,而仅仅只存放一个指针。这样即使是在单线程下也会遇到问题。

vector<typename*>::iterator it1 = my_array.begin();
for( ; it1 != my_array.end(); ++it1 )
{
    vector<typename*>::iterator it2 = my_array.begin();
    for( ; it2 != my_array.end(); )
    {
        if ((*it2) != NULL )
        {
            delete (*it2);
            it2 = my_array.erase(it2);
        }
    }
}

那么上面的代码这时候不可避免的要出现错误,由于上面的代码很少,因此,很容易就知道错了。一般来说服务器的代码都是要长的多,函数之中套函数,一不小心就会犯这样的低级错误。如果我们在编写代码的时候能够加以注意,这种问题也是可以避免的。

如果是2个线程呢?

DWORD WINAPI ThreadProc1( LPVOID lpParam )
{
    vector<typename*>::iterator it = my_array.begin();
    for( ; it != my_array.end(); ++it )
    {
        if ((*it) != NULL )
        {
            (*it)->func();
        }
    }

    return 0;
}

DWORD WINAPI ThreadProc2( LPVOID lpParam )
{
    vector<typename*>::iterator it = my_array.begin();
    for( ; it != my_array.end(); )
    {
        if ((*it) != NULL )
        {
            delete (*it);
            it = my_array.erase(it);
        }
    }

    return 0;
}

很多人会选择加锁,其实大家可以想象,线程ThreadProc1整个函数都在遍历数组,如果加锁其实就等于将整个线程都锁起来,这基本上就等于宣判其他线程的死刑。

其实我们有2个办法来对付。第一问题主要出在指针上,如果我们不用指针而用实例,基本上就不会有这个情况,但这不是问题的关键。问题的关键是我们在什么时候需要变更我们的动态数组,需要确保动态数组在变化的时候不能对该对象进行访问,所以,如果对迭代器加锁就可以避免这种情况,当然带来的问题是我们自己对迭代器要进行改造。

上面的问题只是一种比较简单的现象,还有更为复杂的线程同步在等待着我们,我不知道大家有没有什么好的建议。

2万

主题

2万

帖子

6万

积分

论坛元老

Rank: 8Rank: 8

积分
66489
QQ
发表于 2007-8-25 19:39:00 | 显示全部楼层

Re:头疼的多线程

别思考这问题了!

你直接跳到epoll/kqueue/iocp去吧!

这样你只会痛苦一次。

6

主题

471

帖子

1047

积分

金牌会员

Rank: 6Rank: 6

积分
1047
发表于 2007-8-26 11:25:00 | 显示全部楼层

Re: Re:头疼的多线程

hyzboy: Re:头疼的多线程

别思考这问题了!

你直接跳到epoll/kqueue/iocp去吧!

这样你只会痛苦一次。

用这些也要加锁,消息和连接队列加锁是必定的.
其他游戏数组和队列只能要保证只在一个线程内处理,就可以不用加锁,免去同步带来的问题.

14

主题

118

帖子

118

积分

注册会员

Rank: 2

积分
118
发表于 2007-8-26 13:48:00 | 显示全部楼层

Re:头疼的多线程

读写锁,这是个经典问题。有现成的解决方法。
问题在于:多个线程可同时读,但不可以同时写以及同时读写。

149

主题

4981

帖子

5033

积分

论坛元老

Rank: 8Rank: 8

积分
5033
QQ
发表于 2007-8-27 10:25:00 | 显示全部楼层

Re:头疼的多线程

好久不来了,插嘴一句:多线程什么情况下才成为“必要”?

27

主题

179

帖子

259

积分

中级会员

Rank: 3Rank: 3

积分
259
发表于 2007-8-28 02:01:00 | 显示全部楼层

Re:头疼的多线程

基本上现在是个游戏都需要多线程,别说server了。 关于多线程锁,你可以上网搜索下: lock-free algorithm 或者搜索 lock-free linked list. 这种算法使用 CAS (compare and swap) 或者 DCAS (double word compare and swap) 可是实现读写同时进行。但是需要在删除的时候特别注意。其难点在于一种叫ABA problem的情况。你看几篇paper就会了解。 解决方法是使用iterator. lock free的算法平均性能在高负载的情况下,大约是使用mutex和critical section的3到4倍。 有些测试结果超过10倍的都有。但是还是会慢于不需要lock的情况, 而且算法调试极其复杂,很难debug。下面是对于多线程的一点建议:

1。尽量使用不需要lock的设计。比如对于共享数据尽量只用一个线程访问,然后实现一个product consumer的结构。
2。尽量使用 lock-free queue,然后才是lock-free linked list. 因为lock-free queue没有ABA问题,最容易实现。
3。对于不是性能瓶颈的地方使用mutex或者critical section
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2026-1-25 03:57

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表