.NET CoreCLR开发人员指南(上)

1.为什么每一个CLR开发人员都需要读这篇文章

  和所有的其他的大型代码库相比,CLR代码库有很多而且比较成熟的代码调试工具去检测BUG。对于程序员来说,理解这些规则和习惯写法非常的重要。

  这篇文章让所有的CLR开发者都尽量能在较少知识的情况下,去了解CLR中自己工作的那一部分内容。这篇文章将会为你呈现CLR的发展史,以及不同阶段解决的不同问题和不同阶段解决问题以后给开发者带来的一些更加便利的东西。

1.1代码规范

  这是最为重要的一个章节!设想一下本文的目录里面的一些项,然后想想自己该如何设计代码。这个章节讲会分成2部分,一部分是托管代码,另一部分是非托管代码,不同的部分,将会面临的问题也不同。

  规范是由不变性和团队的一些计划息息相关的。

不变性从语义上来说,是由架构来控制的。举个例子:安全的GC中托管对象的引用在非托管代码中,这是不会出现的,当你违反了这一恒定性的话,对于开发者来说,这就是一个很明显的BUG。

  团队计划规范 是当我们写了一些“非常不错的练习”代码,举个例子:我现在规定每一个方法都必须附带一个契约,如果有方法违反了这个规定,那么除非你可以解释为什么这段代码不用按照规范来办事,否则会造成团队其他成员的一些想法。

  团队计划相对于不变性(架构)来说并不是那么的重要。比如对于你使用safemath.h来说,遵守函数的一些规范远远比你做一个整型上溢校验要重要。但是处于安全考虑,我们通常都会把它放在优先级高的位置。

  有一种规范你不会在这篇文章里找到,那就是代码整洁度,比如大括号摆放的位置,当然这不属于代码范畴。我们也不会强制性要求这些语言上的问题。这篇文章将会介绍到如下内容:

  • 介绍一个实际的存在的BUG

  • 大大增加严重BUG的风险

  • 对于通用BUG自动检测的一些挫败感。

1.2如何插入通用任务

  这一章节 在这个部分可以理解为FAQ,如果你有特别的需要的话,搜一下本文目录中的"最佳训练";如果你想在CLR中添加另一个hash表的实现,那就好好看看这篇文章,文章里有现成的代码,可以适应你的业务需求。

2.代码规范(非托管)

2.1你的代码是否是GC安全的

2.1.1 GC黑洞是如何产生的

  GC黑洞这一术语引用自一个经典的GC BUG。GC黑洞是一种致命的BUG因为对于GC事故来说是很容易被引入的,它很少被复制,而且调试BUG的过程是乏味而且会花费大量的时间去找这个问题。一个简单的GC黑洞可能会让开发和测试人员花费数周的时间去排查问题。

  CLR的主要功能之一就是垃圾回收机制。这意味着当你给对象分配内存空间的时候,如果是受托管的应用程序,则你不用去刻意的去释放掉分配的内存空间。除此之外,CLR会有一个定时器去周期性的执行垃圾回收机制,这时GC会把对象丢弃掉不再使用。与此同时,GC会把HEAP(堆)给收紧,以防止内存中无用黑洞的产生。因此,在托管堆中的对象并没有一个固定的内存地址;因为GC,对象在内存中就像蝌蚪一样不断的变化着自己的位置。

  为了去做这项工作,GC必须告诉所有的GC对象之于它们的引用。GC必须知道每一个元素在栈中的地址;每一个注册的和非GC数据结构的对象的指针对指向一个GC对象。这些外部指针被称为根引用。

写到这里了:

  当你拥有了这些信息以后,GC可以找到从外部GC堆里面直接引用的对象,这些对象会轮流被其他对象所引用。从这些引用延伸开来的话,GC将会找到所有“活的”对象,而所谓的不能被找到的对象(死的对象)将会被丢弃掉。然后GC将会移动这些活的对象以减少内存碎片;如果做了这些工作的话,GC将会更新所有的移动过的对象的引用。

  任何时候一个对象呗分配内存空间的话,GC都将被触发。GC将会显示带一个请求给GarbageCollect 方法,GC不会在这些事件之外被异步调用除了其他运行中的线程会触发GC,这些线程也会异步去触发GC除非你显示调用GC,这些稍后会进行详细的介绍。

  当CLR创建GC的引用的时候回形成GC黑洞。如果我们不让GC知道哪些引用的话,执行一些操作直接或者间接的触发GC,然后尝试使用最初的引用。这时候,引用将会指向垃圾内存,CLR会读取到一些错误的数据,不管引用是指向哪里。

 2.1.2 第一个GC黑洞

  下面的代码将会以最简单的代码来介绍系统中的GC黑洞。

//OBJECTREF 这里是指的 用typedef 来表示对象的指针所指向的地址{MethodTable *pMT = g_pObjectClass->GetMethodTable();OBJECTREF a = AllocateObject(pMT);OBJECTREF b = AllocateObject(pMT);
   //错误,a 可能会指向垃圾内存如果第二个
AllocateObject 触发GC垃圾回收机制
   DoSomething (a, b); 
}


  上面的代码所做的事情只是分配了2段托管代码,然后a和b一起调用了DoSomething方法执行一些逻辑代码。

  上面的代码如果你直接执行的话,看起来是没有什么问题的,但是这段代码最终会爆发出一些问题。为什么呢?因为第二段代码不经意的触发了GC,GC将会把你刚刚赋值的变量a的对象实例抛弃掉。这些代码好比在CoreCLR中的C++代码,是被非托管的编译器编译的,GC并不知道变量a是包含了一个某个对象的根引用并且是不被GC回收掉的对象。

  上面说的这点是值得去重现的。GC并没有根引用存储在本地变量或者或者是非GC数据结构的知识点;对于CLR来说,你必须以正确的方式去运转它,这才是王道。

2.1.3 使用GCPROTECT_BEGIN去保持引用的时效性

  下面的代码告诉如何修复上面的代码所出现的GC黑洞:

#include "frames.h"{MethodTable *pMT = g_pObjectClass->GetMethodTable();    //正确写法OBJECTREF a = AllocateObject(pMT);GCPROTECT_BEGIN(a);OBJECTREF b = AllocateObject(pMT);DoSomething (a, b);GCPROTECT_END();
}

  注意到添加的GCPROTECT_BEGIN这一行文字,GCPROTECT_BEGIN是参数为引用类型的宏,它是完全可以被引用地址&赋值的表达式。GCPROTECT_BEGIN 告诉GC两件事:

  • GC并没有把变量a的引用指向的任意对象丢弃掉。

  • 如果GC移动了和变量a的引用所指向的对象,那么变量a必将指向一块新开辟的内存空间。

  现在如果第二个AllocateObject()方法触发了GC,a对象之后依然会在周围,本地变量a依然会指向a对象。a的地址可能不再和之前保持一致。但是它依然会指向同一个对象,因此DoSomething()的值将会是正确的。

  这里我们注意到我们并没有以同样的方式保护b,因为回调函数并没有在DoSomething执行完成之后使用b,更深入的说,这里并没有指针b保持它的更新状态因为DoSomething方法其实是接受到的是引用的一个拷贝,注意不要和对象的复制混在一起了,它并不是引用了它自己。DoSomething 内部也触发了GC,DoSomething 只负责保护它们自己的a和b的一份拷贝而已。

  就像之前说过的,没人应该抱怨如果你让它能够“安全”和GCPROTECT 变量b。你永远也不知道以后什么时候其他人写的代码会让你的保护变得有一样,所以这是必须的。

  每一个GCPROTECT_BEGIN 都必须有一个GCPROTECT_END以便结束对变量a的保护,作为额外的保护,GCPROTECT_END 重写了变量a以至于让它变成垃圾变量,如果这时候你再使用a就会导致错误的产生。GCPROTECT_BEGIN 和GCPROTECT_END 会产生一个新的C语言作用域级别级别,如果这2个不是成对出现的,那就会抛出异常。

2.1.4 不要在GCPROTECT中使用非局部返回

  永远不要使用return ,goto以及其他非局部返回在GCPROTECT_BEGIN和GCPROTECT_END之间,这会是线程框架链崩溃。

  如果在GCPROTECT 块中抛出一个托管异常(通常是由COMPlusThrow()方法触发的异常),异常的子系统会知道GCPROTECT 并正确修复框架链以解决框架链断裂的问题。

  为什么GCPROTECT 没有从C++智能指针基类而派生出来?因为GCPROTECT起源于.NET Framework 1.0,它其实本质是宏。所有的错误在那时候已经明确被终结了,并没有使用到任何C++的异常处理或者栈内存的分配。

2.1.5 不要在同样的位置使用GCPROTECT 2次

  下面的代码是错误的并且会造成一些不同的崩溃异常:

OBJECTREF a = AllocateObject(...);
GCPROTECT_BEGIN(a);
GCPROTECT_BEGIN(a);

  当然,如果GC足够强大可以忽略掉第二个GCPROTECT,实际上GCPROTECT 是不会被“保护”多次的,这是不可能的。

  不要对引用的拷贝的引用感到迷惑,它保护2次引用是合法的;不正确的是保护了2次引用的拷贝,因此,下面的代码是正确的:

OBJECTREF a = AllocateObject(...);
GCPROTECT_BEGIN(a);
DoSomething(a);
GCPROTECT_END();void DoSomething(OBJECTREF a)
{GCPROTECT_BEGIN(a);GCPROTECT_END();
}

2.1.6 保护多个OBJECTREF

  你可以使用GCPROTECT保护多个OBJECTREF 的地址,但是它受C++多级作用域的限制,设想一下你需要如何用不确定性的时间复杂度在一个非GC数据结构里存储根引用?

  解决方法是OBJECTHANDLE.OBJECTHANDLE 会告诉GC让它分配一块特别的内存块的地址;任何存储在这里的根引用都将在它的生命周期中不会被销毁,如果有对象的移动,那么它的地址将会被更新。你可以间接恢复它的正确的内存地址。

   Handles是多个不同层次首相的实现,通过objecthandle.h暴露使用的一个公共的官方接口;不要对handletable.h 里面包含了这个而感到困惑。CreateHandle() API 方法分配了新的内存空间,ObjectFromHandle()间接引用了handle以及返回了最新的引用,DestroyHandle()释放内存空间。

  下面的代码段告诉了我们如何使用handles,实际上,人们更愿意使用GCPROTECT.

{MethodTable *pMT = g_pObjectClass->GetMethodTable();    // 另一种方法是使用handles.handles会使用更多的内存,对于这么简单的例子    // 如果你想长期保护某个东西,使用handles会有用。    OBJECTHANDLE ah;OBJECTHANDLE bh;ah = CreateHandle(AllocateObject(pMT));bh = CreateHandle(AllocateObject(pMT));DoSomething (ObjectFromHandle(ah),ObjectFromhandle(bh));DestroyHandle(bh);DestroyHandle(ah);
}


  系统为我们提供了不同种类的handles.下面会列举 几个常用的,如果你想查看所有的objecthandle.h里面有 完整的。

  • HNDTYPE_STRONG: 默认的。它的作用和普通引用相等,使用方法:CreateHandle(OBJECTREF).

  • HNDTYPE_WEAK_LONG:在一个对象的生命周期中一直跟踪它的强类型引用而并非是它自身以防止它触发GC。使用方法:CreateWeakHandle(OBJECTREF)。

  • HNDTYPE_PINNED:在一个对象的垃圾回收生命周期里阻止对象引用的移动,对于栈顶的已经添加属性的强handles.当GC启用时,传递指针给运行时之外的内部对象的时候尤为有用。

  注意:如果使用第三个的话,GC垃圾回收最好是一个长周期,因为短期回收会阻止GC装箱而造成内存的不必要的消耗。所以在使用它的时候应该再三考虑。

2.1.8 正确的使用GC模式:抢占式 VS 协同工作式  

  早期,GC其实是不会自动触发的,对于一个已有线程来说,这是对的。但是CLR是多线程生物,如果你的线程一致执行并保持不抛出异常直到结束,那么它和这个进程里的其他线程也是没有任何关系的。

  设想一下有2种不同的方式去执行GC:

  • 抢占式:任何单个线程触发GC,并且这个线程不去关心其他线程的状态,也就是说,其他线程也许某个点时间会和这个线程的GC同事触发。

  • 协同工作式:一个线程只能启动一次GC并且其他线程都要给这个线程开放GC启动的权限,如果当前线程发出了一个GC请求,那么它会被阻塞,直到其他线程都同意这个线程进行GC操作。

  每种不同的模式都有它们自己的优点和缺点,抢占式看起来更有吸引力和效率,除了一件事情发生:完全打破我们之前讨论的GC保护机制。比如下面的代码:

OBJECTREF a = AllocateObject(...)
GCPROTECT_BEGIN(a);
DoSomething(a);

  现在,让我们来看看相对完整的伪代码吧:

call    AllocateObject
mov [A],eax  ;;把结果存储到a中... 省略GCPROTECT_BEGIN的代码 ...
push    [A]        ;把参数传入Dosomething
call    DoSomething

  无论是在何种情况下,这段代码运行新起来都是没问题的,表面上通过GCPROTECT来看。设想一下push指令之后会怎么样呢?其他的线程得到了这个时间片段,开始执行GC并移动A对象。本地变量A将会被正确的更新,但是对于DoSomething()方法的参数(A的一份拷贝)并没有被更新。因此,DoSomething()会接受一个指向旧的引用的指针并造成程序的崩溃。现在我们进一步知道,如果单独使用抢占式GC并不能能满足CLR。

  那么在什么时候选择哪种模式更好呢?协同工作式GC?在这种情况下,上面的那些问题都不会出现,GCPROTECT 也会按照预期工作。不巧的是,CLR不得不和遗留的非托管代码进行交互。设想一下托管的应用程序唤醒等待用户点击按钮返回Win32 MessageBox API的场景,直到用户点击按钮之前,所有在同一进程当中的托管线程将被GC Block住,这显然会影响程序的执行效率。

  因为没有办法单独能满足CLR的需求,所以CLR支持2种方式一起使用,而作为一个开发者来说,只需要相应的切换线程就行了。注意GC调度模式属于独立线程的一个属性,而并不是一个全局的系统属性。  

  精确的说:一个线程在协同工作模式中长时间运行,它保证GC只在线程触发内存分配时候起作用,唤醒可中断的托管代码或者明确的请求GC。其他线程被GC所阻挡;如果一个线程在抢占模式长时间工作,你必须假定GC能再任何时间被其他线程启动以及和其他线程一起运行。

  一个好的经验法则是:一个CLR线程在任意时间运行在协同工作模式中,那么它会运行在托管代码中或者以任意方式操纵对象的引用。一个异常引擎(Exception Engine)运行在抢占模式中通常会运行非托管代码。比如它已经脱离了托管域,那么运行在抢占模式中的进程中的多个线程会从未进入CLR过,许多CLR内部的代码使用的是抢占模式运行。

  如果是运行在抢占模式中,OBJECTREF将严格的不会进行任何干预,说白点,和它没关系了,这时候得到的值是完全不可靠的。实际上,如果你在抢占模式中添加了OBJECTREF那么编译器在编译的时候会检查其“正确性”。在协同工作模式中,因为GC引起其他的线程的block,所以应该减少等待时间的操作,这是提高效率的方式之一。你也必须注意被动等待的临界区段或者信号。

  设置GC模式:一般通常会使用GCX_COOP 和GCX_PREEMP 宏命令。这些宏命令应该作为容器去操作,你必须在你想执行的代码区间的开始去声明它,在作用域之外的本地或非本地的退出,自动还原功能将会强制还原成初始的模式。  

{ // 总是会开启一个新的C++作用域去改变模式    GCX_COOP();Code you want run in cooperative mode
} // 离开作用域以后会还原到改变之前的模式

  如果一个线程处于协同工作模式去调用GCX_COOP()是合法的,GCX_COOP在那种情况下 将会是一个NOP,同样的适用于GCX_PREEMP。

  GCX_COOP 和GCX_PREEMP永远不会抛出异常以及返回非错误状态。

  当然有一种特别的情况对于纯粹的非托管线程(没有任何线程结构的线程),你可以把它理解为永久的处于竞争模式中,因此,如果GCX_COOP 唤醒这种线程那么

GCX_PREEMP会是NOP的一个子系统。

  下面对于特殊情况下将会有一组变体:

  • GCX_MAYBE_*(BOOL):这种只有在参数为TRUE的情况下才会执行,在作用域结束时,是否还原成初始状态取决于BOOL的值是否为TRUE(这一点很重要,不过只有在作用域中,如果模式通过其他的方式进行了改变,通常情况下,这不会发生)。

  • GCX_*_THREAD_EXISTS(Thread*):如果你关心重复过的GetThread()以及容器中选择的空线程,使用高效的版本通过缓存线程指针以及把它传递给所有GCX_*的调用者。你不能使用这个区改变其他的线程的模式,当然你也不能在这里传递NULL。

  在一个方法中多次改变模式,你必须对每次改变使用一个新的作用域,你也可以在还原模式作用域结束之前去调用GCX_POP()方法(这种模式会在作用域结束之前再次进行还原。因为模式还原是幂等的,这不应该被关心),永远不要像下面这样做:

{GCX_COOP();...GCX_PREEMP():  //错误!}

  系统会抛出一个编译错误:变量已经在相同的作用域中被声明过。

  基于容器的宏命令有没有比较好的方式去改变模式呢?有时候需要在超出作用域范围时留下改变的模式,这时候你需要一个未加工的无作用域的方法:

GetThread()->DisablePreemptiveGC();   // 切换到协同工作模式GetThread()->EnablePreemptiveGC();  // 切换到抢占模式

  对于这些方法并没有自动的模式恢复机制,所以管理好它的生命周期就是你的义务了,另外,模式的改变不能被嵌套,如果你改变一个已经有的模式你将会得到一个断言(assert),当前的对象必须是当前执行的线程,而不是其他线程的模式。

  关键点:只要可能的话,使用GCX_COOP/PREEMP比无作用域的调用DisablePreemptiveGC()更好。

  你需要在契约中的特别的模式中使用断言,可以使用如下的模式去做:

CONTRACTL
{MODE_COOPERATIVE
}
CONTRACTL_ENDCONTRACTL
{MODE_PREEMPTIVE
}
CONTRACTL_END

  下面的是独立版本:

{GCX_ASSERT_COOP();
}{GCX_ASSERT_PREEMP();
}


  你会注意到,独立版本相对于简单版本更像一个容器,这么做的目的是容器会在离开作用域之前会再判断一下以保证拆箱操作的正确性。但是退出检查最终因为启用而在初始化的时候在所有的拆箱代码被清理干净前,那么它是被禁用掉的。不巧的是,最终还是没有出现,在你用GCX容器去管理mode的改变时,是不会发生任何问题的。  

参考资料:https://github.com/dotnet/coreclr/blob/master/Documentation/coding-guidelines/clr-code-guide.md#1

原文链接:http://www.cnblogs.com/kmsfan/p/coreclr_guildline.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/328285.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

git 拉取gitlab代码

博客园首页新随笔联系管理订阅 随笔- 71 文章- 2 评论- 0 使用git在gitlab上拉取代码的方法 最近在项目中用到了gitlab,他是一个类似于github的代码托管工具。 因为是第一次使用还不太熟悉,所以在此记录一下。 1、首先需要使用github的注册账号登录gitlab,查…

从oracle里面取直,45个非常有用的 Oracle 查询语句小结

日期/时间 相关查询1.获取当前月份的第一天运行这个命令能快速返回当前月份的第一天。你可以用任何的日期值替换 “SYSDATE”来指定查询的日期。代码如下:SELECT TRUNC (SYSDATE, MONTH) "First day of current month"FROM DUAL;2.获取当前月份的最后一天这个查询语句…

Spring Boot 集成 Mybatis 实现双数据源

转载自 Spring Boot 集成 Mybatis 实现双数据源这里用到了Spring Boot Mybatis DynamicDataSource配置动态双数据源&#xff0c;可以动态切换数据源实现数据库的读写分离。 添加依赖 加入Mybatis启动器&#xff0c;这里添加了Druid连接池、Oracle数据库驱动为例。 <depe…

.Net Core及.Net Standard主要概念回顾

在.Net Core之前&#xff0c;选择编译目标是一个相对简单的操作。现在&#xff0c;开发人员面临多种可能&#xff0c;选择一个目标不再那么显而易见了。要想对.Net Core有一个全面的了解&#xff0c;就要了解两个主要的概念&#xff1a;“目标框架别名&#xff08;Target Frame…

如何在Intellij IDEA中集成Gitlab

如何在Intellij IDEA中集成Gitlab 2018年06月11日 16:05:14 葬月魔帝 阅读数&#xff1a;9747 据说在微软收购github当天&#xff0c;一大批用户纷纷转向了gitlab和bitbucket&#xff0c;这两者也都是比较不错的代码托管网站&#xff0c;针对个人和企业都有对应的免费和收费版…

linux更改桌面壁纸的脚本,自动更换桌面壁纸的脚本,支持Ubuntu 18.04系统

下面提供一个自动更换桌面壁纸的脚本&#xff0c;它支持Ubuntu 18.04、UbuntuKylin 18.04、Ubuntu Mate系统。注意事项&#xff1a;1.默认的壁纸通常在目录路径为/usr/share/backgrounds中&#xff0c;如果不是请自行修改&#xff0c;不影响脚本的使用效果&#xff0c;注意目录…

推荐一个实用的 .gitignore 文件

转载自 推荐一个实用的 .gitignore 文件为什么要忽略文件&#xff1f; 常用的版本控制工具&#xff0c;不管是使用 git 还是 svn&#xff0c;我们都需要排除一些与程序代码无关的文件&#xff0c;如像 eclipse/ intellij idea 等 IDE 工具留下来的 .settings、 .classpath、…

应用工具 .NET Portability Analyzer 分析迁移dotnet core

大多数开发人员更喜欢一次性编写好业务逻辑代码&#xff0c;以后再重用这些代码。与构建不同的应用以面向多个平台相比&#xff0c;这种方法更加容易。如果您创建与 .NET Core 兼容的.NET 标准库&#xff0c;那么现在比以往任何时候都更接近于这一现实。 但是&#xff0c;现有的…

idea安装插件plugin(主要针对网络连接不上的情况)

idea安装插件plugin(主要针对网络连接不上的情况) 2018年04月27日 11:07:36 多机智 阅读数&#xff1a;10097 STEP1: ctrl alt s 打开settings STEP2: 在输入框键入 Plugins STEP3: 输入你想要的插件名称&#xff0c;我这边输入的是nodejs,因为最近在学(我这边是安装过…

linux打开服务iis,如何在Linux中引导时列出启动服务?

根据不同的启动系统&#xff0c;查找启动服务的列表会有所不同。Systemd是主要新版本Linux发行版的默认启动系统。如果您的系统使用systemd系统管理器&#xff0c;您可以使用以下命令列出所有服务。$ sudo systemctl list-unit-files --typeservice如上所述&#xff0c;这个命令…

银行营业网点管理系统——Servlet包(CityAreaServlet )

package BranchesMgr.servlet; /*** 城区表的Servlet*/ import java.io.IOException; import java.io.PrintWriter; import java.util.List;import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; im…

.NET Core中使用Razor模板引擎

一、简介 在MVC以外的场景中&#xff0c;我们往往需要完成一些模板引擎生成代码或页面的工作&#xff1b;在以前我们一般常用的有Razor、NVeocity、VTemplate。虽然所有的模板系统都具有一些共同特征&#xff0c;但 Razor却和我们前面讨论的二种视图引擎截然不同。不同于其它视…

idea中Gitlab项目导入导出

idea中Gitlab项目导入导出 2018年04月16日 16:39:23 蓝之刃 阅读数&#xff1a;15415 Gitlab的使用 Gitlab跟Github类似&#xff0c;都是代码托管的网站&#xff0c;最大的不同是Gitlab创建的项目可以免费私有的&#xff0c;不必像Github那样收费&#xff0c;而且Gitlab还可…

服务器 ha linux,Linux 高可用(HA)集群之Heartbeat详解

大纲一、Heartbeat 的定义二、Heartbeat 的版本与组件三、Heartbeat 的各版本之间的区别四、Heartbeat 集群的一般拓扑图推荐阅读&#xff1a;一、Heartbeat的定义Heartbeat 项目是 Linux-HA 工程的一个组成部分&#xff0c;也是目前开源HA项目中最成功的一个例子&#xff0c;L…

一篇文章彻底了解清楚什么是负载均衡

转载自 一篇文章彻底了解清楚什么是负载均衡 负载均衡是高可用网络基础架构的的一个关键组成部分&#xff0c;有了负载均衡&#xff0c;我们通常可以将我们的应用服务器部署多台&#xff0c;然后通过负载均衡将用户的请求分发到不同的服务器用来提高网站、应用、数据库或其他…

intellij-IDE运行Java程序报错:java: -source 1.5 中不支持 lambda 表达式 有用

intellij-IDE运行Java程序报错&#xff1a;java: -source 1.5 中不支持 lambda 表达式 2017年12月29日 15:04:15 佛空如水 阅读数&#xff1a;795 报错&#xff1a; 解决&#xff1a; 第一步修改&#xff1a; 第二步修改&#xff1a;

linux驱动开发音频设备驱动,linux驱动开发—基于Device tree机制的驱动编写

摘要&#xff1a;媒介 Device Tree是一种用去描绘硬件的数据布局&#xff0c;类似板级描绘说话&#xff0c;发源于OpenFirmware(OF)。正在现在遍及应用的kernel 2.6.x版本中&#xff0c;对分歧仄台、分歧硬件&#xff0c;往]前言Device Tree是一种用来描述硬件的数据结构&#…

gRPC .NET Core跨平台学习

前些天发布gRPC C# 学习&#xff0c;在.NET Framework 中使用gRPC &#xff0c;今天来学习 .NET Core gRPC。 gRPC 的.NET Core 包在NuGet 上发布了&#xff0c;结合.NET Core 实现gRPC 完美跨平台。 本篇主要讲解 .NET Core 版gRPC客户端运行在Ubuntu系统上&#xff0c;与局域…

IDEA导入Maven项目,pom.xml文件中 有inspects a maven model for resolution problems报错 !!!!!!!!!!有用

IDEA导入Maven项目&#xff0c;pom.xml文件中 有inspects a maven model for resolution problems报错 2018年08月06日 22:13:09 东方不能败 阅读数&#xff1a;4616 我是导入一个已经写好的Maven工程&#xff0c;导入后返现在pom.xml文件中有inspects a maven model for res…

Windows Server 2016提供Docker原生运行的企业级支持

Windows Server 2016已原生支持Docker&#xff0c;这使得Windows应用可运行在容器中。该评估版目前已经可用&#xff0c;期望于本月底之前&#xff0c;Microsoft将提供从评估版到RTM的升级路径。该评估版发布于上周的Microsoft Ignite大会上&#xff0c;同时还发布了Microsoft和…