是否有.Net类可以执行的ManualResetEvent.PulseAll()操作(如果存在)?
我需要原子地释放一组等待相同信号的线程.(我并不担心我的预期用法会出现"线程踩踏事件".)
你不能用a ManualResetEvent来做这件事.例如,如果你这样做:
ManualResetEventSlim signal = new ManualResetEventSlim();
// ...
signal.Set();
signal.Reset();
Run Code Online (Sandbox Code Playgroud)
然后根本没有释放等待信号的线程.
如果你Thread.Sleep(5)在Set()和Reset()调用之间放置一个,那么一些但不是所有等待的线程都被释放.将睡眠时间增加到10毫秒可以释放所有线程.(这是用20个线程测试的.)
显然,加入Thread.Sleep()这项工作是不可接受的.
然而,这很容易做到,Monitor.PulseAll()我写了一个小课程来做到这一点.(我写一个类来做这个的原因是我们发现使用Monitor的逻辑虽然相当简单,但是非常明显,不足以让这样的类来简化使用.)
我的问题很简单:在.Net中是否有一个类可以做到这一点?
作为参考,这是我的" ManualResetEvent.PulseAll()"等效的简单版本:
public sealed class Signaller
{
public void PulseAll()
{
lock (_lock)
{
Monitor.PulseAll(_lock);
}
}
public void Wait()
{
Wait(Timeout.Infinite);
}
public bool Wait(int timeoutMilliseconds)
{
lock (_lock)
{
return Monitor.Wait(_lock, timeoutMilliseconds);
}
}
private readonly object _lock = new object();
} …Run Code Online (Sandbox Code Playgroud) 在我开始使用代码约定之前,有时在使用构造函数链时会遇到与参数验证相关的繁琐.
这是一个(人为的)示例最简单的解释:
class Test
{
public Test(int i)
{
if (i == 0)
throw new ArgumentOutOfRangeException("i", i, "i can't be 0");
}
public Test(string s): this(int.Parse(s))
{
if (s == null)
throw new ArgumentNullException("s");
}
}
Run Code Online (Sandbox Code Playgroud)
我希望Test(string)构造函数链接Test(int)构造函数,并使用int.Parse().
当然,int.Parse()不喜欢有一个null参数,所以如果s为null,它将在我到达验证行之前抛出:
if (s == null)
throw new ArgumentNullException("s");
Run Code Online (Sandbox Code Playgroud)
这使得检查毫无用处.
如何解决?好吧,我有时习惯这样做:
class Test
{
public Test(int i)
{
if (i == 0)
throw new ArgumentOutOfRangeException("i", i, "i can't be 0");
}
public Test(string s): …Run Code Online (Sandbox Code Playgroud) 本书CLR Via C#介绍了一种通过二进制序列化克隆对象的简单方法.
它指定StreamingContextStates.Clone何时创建BinaryFormatter类似:
var formatter = new BinaryFormatter
{
Context = new StreamingContext(StreamingContextStates.Clone)
};
Run Code Online (Sandbox Code Playgroud)
文档StreamingContextStates.Clone说它
指定正在克隆对象图.用户可以假设克隆的图形将继续存在于同一进程中,并且可以安全地访问句柄或对非托管资源的其他引用.
很公平 - 但我真的不知道这实际意味着什么.这实际上以什么方式改变了行为BinaryFormatter?任何人都可以列出使用此标志的具体效果吗?
考虑以下枚举声明和int数组:
enum Test { None };
int[] data = {0};
Run Code Online (Sandbox Code Playgroud)
要将此int[]数组转换为Test[]数组,我们可以简单地执行此操作:
Test[] result = Array.ConvertAll(data, element => (Test)element);
Run Code Online (Sandbox Code Playgroud)
我最初试图这样做:
Test[] result = data.Cast<Test>().ToArray();
Run Code Online (Sandbox Code Playgroud)
但是,它会在运行时抛出异常:
System.ArrayTypeMismatchException:无法将源数组类型分配给目标数组类型.
问题一
有没有办法使用Linq并Cast<>没有错误,或者我必须使用Array.Convert()?
问题二(在第一个问题得到正确回答后添加)
究竟为什么不起作用?
看起来这是一个实现细节逃逸的情况......考虑:
这会导致错误:
var result = data.Cast<Test>().ToList(); // Happens with "ToList()" too.
Run Code Online (Sandbox Code Playgroud)
但这不是:
var result = new List<Test>();
foreach (var item in data.Cast<Test>())
result.Add(item);
Run Code Online (Sandbox Code Playgroud)
这也不是:
var result = data.Select(x => x).Cast<Test>().ToList();
Run Code Online (Sandbox Code Playgroud)
明确的含义是在.ToList()实现中正在进行某种优化,导致此异常.
附录:
这也有效:
List<int> data = new List<int>{0};
var result …Run Code Online (Sandbox Code Playgroud) 我们目前正在将我们的代码库从 .Net Framework 4.8 转换为 .Net Core 3.1。
一些代码对性能非常敏感。一个例子是一些应用汉明窗口过滤器的代码;我有点沮丧地发现 .Net Core 3.1 编译的代码比为 .Net Framework 4.8 编译的相同代码的运行速度慢了大约 30%。
我创建了一个多目标 SDK 样式的项目,如下所示:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworkS>net48;netcoreapp3.1</TargetFrameworkS>
<Optimize>true</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
</Project>
Run Code Online (Sandbox Code Playgroud)
本项目代码如下(重要代码在for (int iter = ...循环内):
using System;
using System.Diagnostics;
namespace FooBar
{
class Program
{
static void Main()
{
#if NET48
Console.WriteLine("NET48: Is 64 bits = " + Environment.Is64BitProcess);
#elif NETCOREAPP3_1
Console.WriteLine("NETCOREAPP3_1: Is 64 bits = " + Environment.Is64BitProcess);
#else
Invalid build, so …Run Code Online (Sandbox Code Playgroud) .Net Core定义object.ToString()为public virtual string? ToString();
这意味着如下代码会引发CS8602“取消引用可能为空引用”警告:
object t = "test";
var s = t.ToString();
Console.WriteLine(s.Length); // Warning CS8602
Run Code Online (Sandbox Code Playgroud)
这可以通过编写s!.Length或轻松解决t.ToString()!;,但我的问题是:
null在什么情况下从 的实现返回是正确的object.ToString()?
答案似乎是:“你永远不应该从 object.ToString() 返回 null”,这很公平 - 但这确实提出了另一个问题,即“在这种情况下,微软为什么将其声明为public virtual string? ToString();”?
在旁边:
下面的一些评论表明,由于实现可能会错误地返回 null,因此必须将返回值声明为string?.
如果这是真的,那么为什么相同的逻辑不适用于没有ICloneable.Clone()声明为返回的?object?
这个逻辑肯定适用于返回引用类型的每个接口方法吗?ICustomFormatter.Format()理论上,此类方法的任何实现(例如, )都可以返回null- 因此返回值应该可以为 null。但他们不是。
阅读 DavidG 提供的链接后,我相信该主题中的讨论回答了我满意的问题:
谁能告诉我Plinq的正确代码是什么?我将双数组中每个元素的正弦的绝对值的平方根加起来,但是Plinq给了我错误的结果.
该计划的输出是:
Linq聚合= 75.8310477905274(正确)Plinq聚合= 38.0263653589291(大约应该是它的一半)
我一定是做错了什么,但我找不到什么......
(我在Core 2 Duo Windows 7 x64 PC上运行Visual Studio 2008.)
这是代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
double[] array = new double[100];
for (int i = 0; i < array.Length; ++i)
{
array[i] = i;
}
double sum1 = array.Aggregate((total, current) => total + Math.Sqrt(Math.Abs(Math.Sin(current))));
Console.WriteLine("Linq aggregate = " + sum1);
IParallelEnumerable<double> parray = array.AsParallel<double>();
double sum2 = parray.Aggregate((total, current) => …Run Code Online (Sandbox Code Playgroud) (注意:我使用的是.Net 4,而不是 .Net 4.5,所以我不能使用TPL的DataflowBlock类.)
TL; DR版本
最后,我只是在寻找一种方法来处理使用多个线程的顺序工作项,以便在最终输出中保留它们的顺序,而不需要无限制的输出缓冲区.
动机
我现有的代码提供了一个多线程机制来处理多个数据块,其中一个I/O绑定线程("供应商")负责排队数据块以进行处理.这些数据块包括工作项.
一个或多个线程("处理器")负责一次使一个工作项出列,然后处理这些工作项,然后在将其下一个工作项出列之前将处理后的数据写入输出队列.
最终的I/O绑定线程("使用者")负责从输出队列中出列已完成的工作项并将它们写入最终目标.这些工作项目(并且必须)按照它们入队的顺序编写.我使用并发优先级队列实现了这一点,其中每个项目的优先级由其源索引定义.
我正在使用这种方案对大数据流进行一些自定义压缩,其中压缩本身相对较慢但是未压缩数据的读取和压缩数据的写入相对较快(尽管受I/O限制).
我以64K的相当大的块处理数据,因此管道的开销相对较小.
我目前的解决方案运行良好,但它涉及6年前使用许多同步事件编写的大量自定义代码,并且设计看起来有点笨拙; 因此,我开始学习练习,看看是否可以使用更现代的.Net库重写它.
新设计
我的新设计使用了这个BlockingCollection<>类,并且基于这篇微软文章.
特别是,请查看标题为使用多个生产者进行负载平衡的部分.我尝试过使用这种方法,因此我有几个处理任务,每个任务都从共享输入BlockingCollection获取工作项,并将完成的项写入自己的BlockingCollection输出队列.
因为每个处理任务都有自己的输出队列,所以我试图用它BlockingCollection.TakeFromAny()来排队第一个可用的已完成工作项.
多路复用器问题
到目前为止一切顺利,但现在问题来了.微软文章指出:
差距是个问题.管道的下一个阶段,即显示图像阶段,需要按顺序显示图像,并且序列中没有间隙.这是多路复用器的用武之地.使用TakeFromAny方法,多路复用器等待来自两个过滤器阶段生产者队列的输入.当图像到达时,多路复用器查看图像的序列号是否是预期序列中的下一个.如果是,则多路复用器将其传递到显示图像阶段.如果图像不是序列中的下一个图像,则多路复用器将值保存在内部先行缓冲区中,并对没有前瞻值的输入队列重复获取操作.该算法允许多路复用器以确保顺序排序而不对值进行排序的方式将来自传入生成器队列的输入组合在一起.
好吧,所发生的是处理任务可以几乎任何顺序生成成品.多路复用器负责以正确的顺序输出这些项目.
然而...
想象一下,我们有1000件待处理的物品.进一步想象一下,由于一些奇怪的原因,第一个项目需要更长的时间来处理所有其他项目的组合.
使用我当前的方案,多路复用器将继续读取和缓冲来自所有处理输出队列的项目,直到它找到它应该输出的下一个.由于它等待的项目(根据我的"想象如果")仅在处理完所有其他工作项后才出现,我将有效地缓冲整个输入中的所有工作项!
数据量太大,无法实现.当输出队列达到某个最大大小时(即它是一个有界的输出队列),我需要能够阻止处理任务输出已完成的工作项.除非工作项碰巧是多路复用器正在等待的工作项.
那就是我有点陷入困境的地方.我可以想到很多方法来实际实现它,但它们似乎都过于复杂,以至于它们并不比我想要替换的代码更好!
我的问题是什么?
我的问题是:我是以正确的方式来做这件事的吗?
我本以为这是一个众所周知的问题,但是我的研究只发现了一些文章似乎忽略了如果一个工作项与其他所有工作项相比需要很长时间而发生的无限缓冲问题.
有人能指出任何描述合理实现方法的文章吗?
TL; DR版本
最后,我只是在寻找一种方法来处理使用多个线程的顺序工作项,以便在最终输出中保留它们的顺序,而不需要无限制的输出缓冲区.
我一直想知道 - 在c#中有一种方法可以为类中的几个属性定义"模板".这就是我的意思:让我们假设我有以下课程
class MyCLass
{
public int IntVal1 { get {...}; set{...} }
public byte IntVal2 { get {...}; set{...} }
....
public long IntValN { get {...}; set{...} }
}
Run Code Online (Sandbox Code Playgroud)
我没有在get和set访问器中编写任何特定的实现,但是想法是所有这些属性都有非常相似的实现 - 不同之处可能是它们在类的不同成员上运行,这些成员具有不同的类型,但总的来说它们都是看起来像.
我的想法是找到一种方法来定义某种(让我们称之为)"模板",其中一些参数可能用于声明所有这些属性,而无需编写每个属性的实际实现 - 也许使用属性!?!
我想我需要的是类似于C宏.
提前10倍
考虑以下代码:
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
#nullable enable
namespace ConsoleApp1
{
class Program
{
static void Main()
{
var list = makeList();
var weakRef = new WeakReference(list[0]);
list[0] = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine(weakRef.IsAlive);
}
[MethodImpl(MethodImplOptions.NoInlining)]
static List<int[]?> makeList()
{
return new List<int[]?> { new int[2] };
}
}
}
Run Code Online (Sandbox Code Playgroud)
False.True.是什么导致了这种行为差异?(这导致我们的一些单元测试失败。)
注意:我将列表初始化放入makeList()并关闭内联,试图使.Net Core版本与.Net Framework版本相同,但无济于事。
[编辑] 正如汉斯指出的,添加一个循环可以解决这个问题。
将打印以下代码False:
var list = makeList(); …Run Code Online (Sandbox Code Playgroud) c# ×10
linq ×2
.net ×1
.net-4.8 ×1
generics ×1
ienumerable ×1
macros ×1
performance ×1
plinq ×1
properties ×1