我们有一个非常高性能的多任务处理,近乎实时的C#应用程序.这种性能主要通过在内部与本土调度程序一起实施协作式多任务处理来实现.这通常被称为微线程.在该系统中,所有任务都通过队列与其他任务进行通信.
我们所遇到的具体问题似乎只能通过C#不支持的一流延续来解决.
具体而言,问题出现在处理队列的2个案例中.每当任何特定任务在将项目放入队列之前执行某些工作时.如果队列已满,该怎么办?
相反,一个不同的任务可能会做一些工作,然后需要从队列中取出一个项目.如果那个队列是空的怎么办?
我们已经在90%的情况下通过将队列链接到任务来解决这个问题,以避免在任何出站队列已满或入站队列为空时调用任务.
此外,某些任务被转换为状态机,因此如果队列已满/空,它们可以处理并继续而无需等待.
真正的问题出现在一些边缘情况下,其中任何一种解决方案都是不切实际的.在这种情况下的想法是在该点保存堆栈状态并切换到另一个任务,以便它可以完成工作,并随后在能够继续时重试等待任务.
在过去,我们尝试将等待任务调用回调度(递归)以允许其他任务以及稍后重试等待任务.然而,这导致了太多的"僵局"情况.
有一个自定义CLR主机的示例,使.NET线程实际上作为"光纤"运行,这实际上允许在线程之间切换堆栈状态.但现在我似乎无法找到任何示例代码.此外,似乎需要一些重要的复杂性才能做到正确.
有没有人有任何其他创意,如何有效地切换任务,避免上述问题?
是否有其他CLR主机提供此商业或其他?是否有任何附加本机库可以为C#提供某种形式的延续?
需要符合以下规范的git工具.一个人已经存在吗?如果没有,我将创建一个脚本并使其在GitHub上可供其他人使用或贡献.有没有一种完全不同的更好的方法来解决构建/测试每个提交到git存储库中的分支的需要?不仅是最新的,而且每个人都回到了一个起点.
背景:我们的开发环境使用独立的持续集成服务器,非常棒.但是,仍然需要在每个开发人员的PC上进行本地完全构建,以确保在推送到CI服务器时提交不会"破坏构建".不幸的是,通过自动单元测试,这些构建强制开发人员每次等待10或15分钟进行构建.
为了解决这个问题,我们在每台开发者PC上设置了一个"镜像"git存储库.所以我们在主存储库中开发,但任何时候都需要本地完整构建.我们在镜像存储库中运行几个命令来获取,检出我们想要的提交和构建.它的工作非常可爱,所以我们可以继续在主要工作中进行并行构建.
现在只有一个主要问题.我们希望确保每个提交都构建并通过测试.但是我们经常忙着疏忽建立几个新的提交.然后,如果构建失败,则必须进行二等分或手动构建每个临时提交以确定哪个中断.
此工具的要求:
该工具将查看另一个repo,默认情况下为origin,获取并将分支中的所有提交与2个提交列表进行比较.一个列表必须保留成功构建的提交,其他列表提交失败.
它标识任何列表中尚未提交的提交,并开始按照提交的顺序在循环中构建它们.它在第一个失败时停止.
该工具在尝试构建每个提交后,将每个提交适当地添加到成功或失败的列表中.
该工具将忽略成功列表中最早提交之前的任何"遗留"提交.这个逻辑使得下一个点的起点成为可能.
初始点.该工具构建一个特定的提交,如果成功,它将被添加到成功列表中.如果它是成功列表中最早的提交,它将成为"起点",以便在此之前的任何提交都不会检查构建.
只有线性树支持?就像bisect一样,这个工具最适用于提交树,至少从它的起点开始,线性没有任何合并.也就是说,它应该是一个完全通过rebase和快进提交构建和更新的树.
如果它在分支中的一次提交失败,它将停止而不构建在该分支之后的其余部分.相反,如果只是转移到另一个分支,如果有的话.
默认情况下,该工具必须执行一次这些步骤,但允许参数循环使用选项来设置循环之间的秒数.像Hudson或CruiseControl这样的其他工具可以做更多花哨的调度选项.
该工具必须具有良好的默认值,但允许可选控制.
哪个回购?默认情况下来源.
哪个分支?所有这些都是默认的.
什么工具?默认情况下,由存储库的根文件夹中名为"buildtest","buildtest.sh""buildtest.cmd"或"buildtest.exe"的用户提供的可执行文件.
循环延迟?默认情况下运行一次,选项在迭代之间经过几秒后循环.
git build-automation continuous-integration build-process unit-testing
好的,以下链接警告说讨论使用不受支持和未记录的apis.好吧,我试图以任何方式使用代码示例.它主要起作用.关于以下与例外有关的具体问题的任何想法?
http://msdn.microsoft.com/en-us/magazine/cc164086.aspx
仅供参考,我对原始样本做了改进.它维持着指向"previousfiber"的指针.相反,下面更新的示例使用"mainfiber"指针,该指针将传递给每个光纤类.这样,它们总能回到主纤维上.这允许主光纤处理所有其他光纤的调度.其他纤维总是"屈服"回主纤维.
发布此问题的原因与在光纤内抛出异常有关.根据文章,通过使用带有CreateLogicalThreadState(),SwitchOutLogicalThreadState()等的CorBindToRunTime API,框架将为每个光纤创建一个托管线程并正确处理异常.
但是,在所包含的代码示例中,它具有UUnit测试,该测试通过在光纤内抛出托管异常并在同一光纤内捕获它来进行实验.那柔软的作品.但是在通过记录消息处理它之后,堆栈似乎处于错误状态,因为如果光纤调用任何其他方法甚至是空方法,则整个应用程序崩溃.
这对我来说意味着SwitchOutLogicalThreadState()和SwitchInLogicalThreadState()可能没有正确使用,否则他们可能没有做好自己的工作.
注意:问题的一个线索是托管代码注销了Thread.CurrentThread.ManagedThreadId,并且每个光纤都是相同的.这表明CreateLogicalThreadState()方法并没有像宣传的那样真正创建新的托管线程.
为了更好地分析这个,我制作了一个伪代码列表,其中列出了用于处理光纤的低级API的顺序.请记住,光纤都在同一个线程上运行,因此没有任何同时发生的事情,这是一个线性逻辑.当然,必要的技巧是保存和恢复堆栈.这就是它似乎遇到麻烦的地方.
它最初只是一个线程,然后它转换为光纤:
现在第一次调用光纤,它的启动方法执行此操作:
最终纤维需要回到主纤维:
主光纤也将恢复预先存在的光纤:
以下是fibers.cpp,它包装了托管代码的光纤api.
#define _WIN32_WINNT 0x400
#using <mscorlib.dll>
#include <windows.h>
#include <mscoree.h>
#include <iostream>
using namespace std;
#if defined(Yield)
#undef Yield
#endif
#define CORHOST
namespace Fibers {
typedef System::Runtime::InteropServices::GCHandle GCHandle;
VOID CALLBACK unmanaged_fiberproc(PVOID pvoid);
__gc private struct StopFiber {};
enum FiberStateEnum {
FiberCreated, FiberRunning, FiberStopPending, FiberStopped
}; …
Run Code Online (Sandbox Code Playgroud) 伙计们,这对你来说是一个棘手的问题!
TickZoom系统的一部分必须将每种类型对象的实例收集到Dictionary <>类型中.
它们的相等和哈希代码必须基于对象的实例,这意味着引用相等而不是值相等.挑战在于系统中的某些对象已重写Equals()和GetHashCode()以用作值相等,其内部值将随时间而变化.这意味着他们的Equals和GetHashCode是无用的.如何解决这个问题而不是侵扰性?
到目前为止,我们创建了一个结构来包装每个名为ObjectHandle的对象,以便散列到Dictionary中.如下所示,我们实现了Equals(),但仍然存在如何计算哈希码的问题.
public struct ObjectHandle : IEquatable<ObjectHandle>{
public object Object;
public bool Equals(ObjectHandle other) {
return object.ReferenceEquals(this.Object,other.Object);
}
}
Run Code Online (Sandbox Code Playgroud)
看到?有一个方法object.ReferenceEquals(),它将比较引用相等性,而不考虑对象中任何被重写的Equals()实现.
现在,如何通过仅考虑引用而不考虑任何重写的GetHashCode()方法来计算匹配的GetHashCode()?
啊,我希望这给你一个有趣的谜题.我们被困在这里.
真诚的,韦恩
伙计们,我已经编写了20多年的高速软件,并且几乎了解了本书中的每一个技巧,从微型工作台制作合作,分析,用户模式多任务处理,尾递归,你在Linux,Windows上为它提供了非常高性能的东西. , 和更多.
问题是,当CPU密集型工作的多个线程暴露给多核处理器时,我发现自己会感到困惑.
在线程之间(在不同内核上)共享日期的各种方式的微观基准中的性能结果似乎不符合逻辑.
很明显,核心之间存在一些"隐藏的交互",这与我自己的编程代码并不明显.我听说过L1缓存和其他问题,但这些对我来说是不透明的.
问题是:我在哪里可以学到这些东西?我正在寻找一本关于多核处理器如何工作,如何编程以利用其内存缓存或其他硬件架构的深度书,而不是受到它们的惩罚.
任何建议或伟大的网站或书籍?经过大量的谷歌搜索,我空了.
真诚的,韦恩
在一个性能非常高的应用程序中,我们发现CPU可以比双精度计算更长的运算速度.然而,在我们的系统中,我们确定我们从不需要超过9位小数的精度.所以我们使用long来进行所有浮点运算,并且可以理解9点精度.
但是,在系统的某些部分,由于可读性与双打一起工作,因此更方便.因此,我们必须将假设9位小数的长值转换为double.
我们发现简单地将长度除以10除以9的幂或乘以1除以10得到9的幂给出不精确的表示.
为了解决这个问题,我们使用了Math.Round(value,9)
给出精确的值.
但是,Math.Round()
表现的可怕性很慢.
所以我们现在的想法是直接将尾数和指数转换为double的二进制格式 - 因为这样就不需要舍入.
我们已经在线学习了如何检查双尾的位以获得尾数和指数,但是如何通过使用位来弄清楚如何反转它以获取尾数和指数以及制造双精度令人困惑.
有什么建议?
[Test]
public unsafe void ChangeBitsInDouble()
{
var original = 1.0D;
long bits;
double* dptr = &original;
//bits = *(long*) dptr;
bits = BitConverter.DoubleToInt64Bits(original);
var negative = (bits < 0);
var exponent = (int) ((bits >> 52) & 0x7ffL);
var mantissa = bits & 0xfffffffffffffL;
if( exponent == 0)
{
exponent++;
}
else
{
mantissa = mantissa | (1L << 52);
}
exponent -= 1075;
if( mantissa …
Run Code Online (Sandbox Code Playgroud) TickZoom是一款性能非常高的应用程序,它使用自己的并行化库和多个操作系统线程来平滑地利用多核计算机.
该应用程序遇到瓶颈,用户需要从单独的O/S线程向LogAppender写入信息.
FileAppender使用MinimalLock功能,以便每个线程可以锁定并写入该文件,然后释放它以供下一个要写入的线程使用.
如果MinimalLock被禁用,log4net将报告有关已被另一个进程(线程)锁定的文件的错误.
log4net执行此操作的更好方法是使用单个线程来处理写入FileAppender,而任何其他线程只是将其消息添加到队列中.
这样,可以禁用MinimalLock以大大提高日志记录的性能.
此外,该应用程序执行大量CPU密集型工作,因此它还可以提高性能,使用单独的线程写入文件,以便CPU永远不会等待I/O完成.
所以问题是,log4net是否已经提供此功能?如果是这样,你如何启用线程写入文件?是否还有另一个更高级的追加者?
如果没有,那么由于log4net已经包含在平台中,因此可以在TickZoom代码中为此目的实现单独的线程和队列.
真诚的,韦恩
编辑:
谢谢,似乎答案指向开发我们自己的解决方案,可能是某种方式的log4net的扩展.他们清楚地表明log4net不会做这类事情.
此外,我们刚刚意识到我们可能"滥用"日志系统,该系统主要用于通知重要事件或调试信息的人类可读消息.软件输出的这一特定部分仅用于验证系统准确性的自动化工具.
当然,我们也以"正常"方式使用log4net进行调试,警告等.
但这些更像是"事务日志",而不是调试或用户通知日志.更具体地说,这些日志不必直接是人类可读的.如果需要,某种"查看器"可以以ASCII格式显示内容.
因此我们计划将这些事务类型的日志写入高速二进制存储.
谢谢,以下两个答案似乎都非常适合开发我们自己的解决方案.
以下nunit测试比较了运行单个线程与在双核机器上运行2个线程之间的性能.具体来说,这是一台运行在四核Linux SLED主机上的VMWare双核虚拟Windows 7计算机,戴尔Inspiron 503.
每个线程简单地循环并递增2个计数器,addCounter和readCounter.此测试是对Queue实施的原始测试,该实施被发现在多核机器上表现更差.因此,在将问题缩小到可重复性较小的代码时,你在这里没有只增加变量的队列,而且令人震惊和沮丧,它只有2个线程然后一个慢得多.
运行第一个测试时,任务管理器显示1个核心100%忙于另一个核心几乎空闲.这是单线程测试的测试输出:
readCounter 360687000
readCounter2 0
total readCounter 360687000
addCounter 360687000
addCounter2 0
Run Code Online (Sandbox Code Playgroud)
你会看到超过3.6亿的增量!
接下来,双线程测试显示在整个5秒的测试持续时间内两个内核100%忙碌.但是它的输出只显示:
readCounter 88687000
readCounter2 134606500
totoal readCounter 223293500
addCounter 88687000
addCounter2 67303250
addFailure0
Run Code Online (Sandbox Code Playgroud)
这只是2.23亿读取增量.什么是上帝的创造是那些2 CPU在5秒钟内完成的工作少了?
任何可能的线索?你可以在你的机器上运行测试,看看你是否得到不同的结果?一个想法是,VMWare双核性能可能不是您所希望的.
using System;
using System.Threading;
using NUnit.Framework;
namespace TickZoom.Utilities.TickZoom.Utilities
{
[TestFixture]
public class ActiveMultiQueueTest
{
private volatile bool stopThread = false;
private Exception threadException;
private long addCounter;
private long readCounter;
private long addCounter2;
private long readCounter2;
private long addFailureCounter;
[SetUp]
public void Setup() …
Run Code Online (Sandbox Code Playgroud) 在搜索了stackoverflow和googling很多之后,为DynamicMethods调试代码提供的调试解决方案似乎已经过时且非常笨拙.
当然,在LCG(轻量级代码生成)发布后的4年或更长时间内,有人必须找到更好的方法.
您发现什么是验证您编写和调试它的动态IL的最简单方法?
你使用peverify或ILDasm还是别的什么?这两个工具需要将程序集写入磁盘,但DynamicMethod不提供任何直接的方法.
显然,WinDbg aso提供了一种查看IL的方法,但处理这个问题非常尴尬.
像VisualStudio 2010这样的插件将是理想的选择.
有任何想法吗?
我们使用log4net库和字符串格式方法调用编写了数千条不同的日志消息.
这样做的一个问题是,如果字符串格式参数(如{0},{1},{2}具有正确的方法参数计数),则仅在运行时才会出现错误.
许多这些方法仅在自动测试期间很少或从未发生的错误条件下调用.
那么是否有任何工具可以扫描所有C#代码并通知任何字符串格式问题?
注意:我在静态代码分析中看到了许多其他问题,这些问题看似重复,但在查看其中一些工具后,它们似乎无法处理这种特定情况.
如果没有什么真正的好处,我们将在内部编写此工具.
c# ×9
.net ×3
c++ ×1
cpu ×1
debugging ×1
double ×1
equals ×1
exponent ×1
fibers ×1
fileappender ×1
gethashcode ×1
git ×1
hash ×1
log4net ×1
mantissa ×1
multitasking ×1
queue ×1
reference ×1
unit-testing ×1
vmware ×1