问个奇怪的问题,突发奇想,函数调用跟多线程有没有关系?

hwyqy 2015-08-12 09:46:51
我写了个DLL,里面有个类Helper,放了很多函数,仅有函数和函数内的局部变量,没有任何private或public成员变量, 供ASP.Net调用,使用频繁
普通方法
在N多个page页中
使用Helper h=new Helper (),然后h.fun1() ,h.fun2()调用 ,这当然没问题,

在App_Code中,新建一个App类,其中有段代码
public Helper h=new Helper();
然后在所有的页中,就不需要定义变量new一个实例
只要App.Helper.fun1(),App.Helper.fun1(),
也即所有的Asp.net线程共享静态变量 h

所有的线程共享一个静态变量肯定不是好的方法,但方法二,编码方便,而且还可以省去page中不停的new,然后垃圾回收的问题,因为我的Helper类仅是函数体,没有成员变量,不会导致多线程访问的变量冲突
但我想的是,第二种方法会不会引起效率上的问题,方法一中,如果是4核CPU,4个线程可以同时new Helper(),然后同时执行里面的方法,但方法二,4个线程同时访问变量h,会不会需要阻塞其中3个,不能同时调用?应该也能的啊

想不明白了,还请高手指点下
...全文
152 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
myhope88 2015-08-13
  • 打赏
  • 举报
回复
路过,来学习的
by_封爱 2015-08-13
  • 打赏
  • 举报
回复
我到是觉得LZ基本的东西还搞不懂 就在网页里玩多线程 甚至并行..
hwyqy 2015-08-13
  • 打赏
  • 举报
回复
哇,贴子进跑进来好多大大,码了这么多字,感动 我也感觉应该使用静态方法就行,但问题是这些代码是靠工具自动生成,而且会反复生成,已经不可能去改成静态了 通过仔细地看,明白了,我的代码这么写没什么,也学到了不少知识
hwyqy 2015-08-13
  • 打赏
  • 举报
回复
引用 7 楼 diaodiaop 的回复:
我到是觉得LZ基本的东西还搞不懂 就在网页里玩多线程 甚至并行..
我是业余开发人员,所以才说突发奇想^_^
winnowc 2015-08-13
  • 打赏
  • 举报
回复
其实就一个问题,你的Helper类是否是线程安全的。没有使用成员变量也并不意味其线程安全,因为方法内仍然可能直接或间接的使用了共享的数据 / 资源,这需要仔细检查。如果能够确保完全是线程安全的,那就可以使用静态或者全局唯一实例的方式。 而如果不是,那要不然自己进行线程同步,要不然更 简单 的方案是使用线程局部存储(TLS)的方式隔离每个线程使用的实例。比如使用 ThreadStaticAttribute 或是 .net 4.0 里更好用的 TheadLocal<T> 类。 不过这仍然需要考量一些东西,因为 ASP.NET 有 thread agility 优化,并不是完全一个请求一个线程的,一个请求的生命周期中可能会用到多个线程池线程,自己的代码也可能使用其它线程,所以请求和线程不是一一对应的。如果你的代码跟请求中的信息无关,那么用TLS是简单的办法,否则要不然还是需要手动同步,要不然可以在 BeginRequest 这类事件中创建实例,放到 HttpContext.Current.Items 里面,HttpContext 是真正和请求一一对应的,它底层使用的是 CallContext 机制。 但是,就算如此线程隔离或者请求隔离,如果代码里面使用了非线程安全的静态方法(一般 .net 自身的静态方法都是线程安全的,当然最好还是仔细参考文档,然而自己写的和第三方库就无法确保了),或者使用了共享的外部资源,那还是需要同步。比如可能同时写同一个文件,不同步显然会造成写出的数据混乱。 综上,根本问题是确保代码线程安全,如果能保证,就可以静态或全局唯一实例,否则最简单也最不安全的办法是TLS,稍复杂稍安全的办法是HttpContext,最复杂但最安全的办法就是手动同步把它改造成线程安全的。(更复杂的场景下,也可以按需结合使用以上三种方式)
  • 打赏
  • 举报
回复
如果是仅仅按照你描述的设计逻辑,那么实际上我会把 Helper 先设计成为static class,里边的所有方法都是 static 的。这样引用起来方便。 但是我不怕重构。我曾经有一天下午改了一个成熟且许多大企业都已经在用的系统中的500多处代码,就是为了把一个static class 改为需要实例化的class。虽然我觉得挺累的,但是我觉得其实也很值得。 只不过我不随便因为个人爱好而重构,一定要达到一定目的。这通常是因为设计决策改变了。例如你的 Helper 突然要为4、5种不同的数据库而继承同一套接口(显然你有更高级的程序要依赖这套接口),这时候就需要把 static class 中的 static 语句和方法定义上的static关键字。 但是如果你在6个月内不可能立刻就要扩展到那么复杂的多数据库系统,那么我就会写上 static class。我不怕重构时去修改500行代码,因为那是非常简单非常单调的工作,在vs的编译器给你发现的语法错误的指引下就能改完,然后就能跑一遍测试程序证明大系统仍然可以验收通过。
moonwrite 2015-08-12
  • 打赏
  • 举报
回复
.net的类库Math 就全部是静态方法 看看设计模式 推荐《head first 设计模式》 你就会明白为什么为什么要用实例了 比较在C语言的年代 都是函数编程 基本上都是静态的~ 而后来要发展成面向对面的 还是有原因的,多看看书 线程问题 如sp1234说的 看看操作系统的知识
  • 打赏
  • 举报
回复
比如说你把程序的一行行代码看作是一个个垃圾桶,而线程看作是几个捡破烂的人,操作系统为每一个线程内部上下文维护了一个“指针”,使得他知道自己“翻哪一个垃圾桶了”。而且每一个线程上下文都有自己的堆栈,知道每一次启动线程的 h.fun1() 方法的独立的输入输出参数,所以这几个减破烂的人就会去凡所有的垃圾桶,根据自己的输入指令(例如每一个人不同的捡垃圾的规则)操作并且将捡到垃圾的结果也压入线程堆栈来返回。 整个过程中,这几个线程是执行在同一个程序段上的,它们各自有自己的指针、自己的堆栈。你根本不用担心他们会不因为一个人儿阻塞其它人。 实际上,假设你有4核心物理CPU,你创建7个线程去执行 h.fun1() 方法也是一样逻辑。有些人硬要去纠结有几个CPU来配几个线程数量的问题,这只是在很低级的程序中才可能去纠结,在算法逻辑设计中则应该是实现一种高级策略——不管有几个CPU都要使用比较合理的兵法多线程算法。
  • 打赏
  • 举报
回复
这是 windows 操作系统基本知识问题,其实不是 .net 问题。如果是学过软件专业的,这会在操作系统课程中学到,而不是在某种编程语言课程中学到相关知识。 windows 桌面系统是抢先式多线程的系统。假设只有一个CPU,而系统中有(比如说)50个进程(包括系统进程)、这50个进程共有210个线程,那么windows 会分时地去抢占每一个线程的时间片,不会让任何线程独占cpu,每秒钟切换上百次,同一时间只有一个线程真正被执行,结果你就会看到似乎各个线程好像是同时再运行。而假设你有4个CPU,那么自然地,不但可以分时去抢占时间片,而且可以同时运行4个线程,同一时间段所能完成的任务数就增加了。 你可以注意到,这里的操作系统线程调度机制锁针对的对象是“线程”,每一个线程都有一个程序运行的指针,而不管这些代码是不是在同一块内存区域上的。更跟所执行的代码会访问什么 .net 对象毫无关系! 你的变量 h 引用一个对象,如果你把 h.fun1() 分别在不同线程上执行,它就会被并发执行。跟“有几个对象”无关。
  • 打赏
  • 举报
回复
如果你的helper内的方法存在全局变量,而且该变量受方法影响,那多线程会有问题,如果没有什么参数的话,那基本可以认为是安全的,话说helper一般也就是一个个帮助方法而已,这时候完全可以写成static方法
近日灵感飙升,突发奇想,模拟操作系统调度进程的方式写了个调度函数的类库,暂称为TaskThread,此类皆组合或继承自TTaskThread类,它们是一种奇妙的类,它们可以把一个一个的现成函数当成一个“process"强行塞入TaskThread的线程运行,用户不再需要额外初始化线程类。本函数库甚至能模拟操作系统的方式对“任务函数”进行调度运行,目前的策略支持批处理和并行模式,类似OS的批处理文件运行和多任务并发执行。为了便于C++用户者使用,除了对普通函数实现了“任务化”管理之外,本类库所有任务类均实现了对C++类函数作“任务化”管理的TClassTaskThread另一系列版本。 TTaskThread类库总共包括了大大小小几十个类,提供了跨平台(目前只实现的windows)的线程、互斥等基础类,主要实现功能的类是TClassTaskThread系列类,最多带9个参数的函数或类函数的任务管理,以下是类继承关系。 以下是类继承关系图 -TThreadMutex 互斥类 -TSimpleThread 基础线程类 -TClassTaskThread 运行类函数的任务类基类 -TClassNoRetNoArgTaskThread -TClassNoRet1ArgTaskThread -TClassNoRet2ArgTaskThread ...直到支持9个参数的无返回类函数任务类 -TTaskThread 运行普通函数任务类基类 -TNoRetNoArgTaskThread -TNoRet1ArgTaskThread -TNoRet2ArgTaskThread ......直到支持9个参数的无返回函数任务类 -TMutiTaskThread9BatchFacet 批处理运行适应类 -TMutiTaskThread9ParallelFacet并行运行适应类 -TClassMutiTaskThread9BatchFacet 批处理运行适应类 (类函数) -TClassMutiTaskThread9ParallelFacet并行运行适应类 (类函数) -TMutiTaskThread9 多任务管理器类 -TClassMutiTaskThread9 多任务管理器类 (类函数) -TMutiTaskThread9 TBatchTaskThreads批处理管理器类 -TMutiTaskThread9 TParallelTaskThreads并行管理器类 -TClassMutiTaskThread9 TClassBatchTaskThreads批处理管理器类 (类函数) -TClassMutiTaskThread9 TClassParallelTaskThreads并行管理器类 (类函数) 源代码因为大量使用模板、类函数指针等高级的C++特性,因此需要比较新版,接近C99标准的C++编译器,笔者是使用Borland C++ 6编译通过。 以下是一个管理批处理任务的使用示例: int c=0; void AsignC() { c=a+b; } int negtive(int d) { return -d; } TBatchTaskThreads tbth9; tbth9.PushBackTaskNoRet(AsignC); tbth9.PushBackTaskRet(negtive,112); tbth9.PushBackTaskRet(negtive,9); tbth9.Start(); tbth9.WaitForMutiTask(); ShowMessage(IntToStr(GetTickCount()-curtime)); ShowMessage(IntToStr(c)); int testi=tbth9.GetRunedRet(1); ShowMessage(IntToStr(testi)); testi=tbth9.GetRunedRet(2); ShowMessage(IntToStr(testi)); tbth9.ClearTask(); 可以看出,用户只需要把待批处理运行的函数按次序塞入tbth9,然后运行Start,再WaitForMutiTask,之后就可以按照需要取回函数运行结果。可见,TBatchTaskThreads 调用方式是非常简单的。 并行方式的调用方式类似,不再赘述。 以下是完整的源代码,有兴趣者可以复制并散播,但请不要去掉文件头的版权信息,请尊重作者的精神享有权。 笔者手中还有完整的C++Builder 6的DEMO演示例程,如有兴趣,请email至superyys@163.com索取,绝不吝啬。

62,046

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术交流专区
javascript云原生 企业社区
社区管理员
  • ASP.NET
  • .Net开发者社区
  • R小R
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

.NET 社区是一个围绕开源 .NET 的开放、热情、创新、包容的技术社区。社区致力于为广大 .NET 爱好者提供一个良好的知识共享、协同互助的 .NET 技术交流环境。我们尊重不同意见,支持健康理性的辩论和互动,反对歧视和攻击。

希望和大家一起共同营造一个活跃、友好的社区氛围。

试试用AI创作助手写篇文章吧