我发布了部分堆栈跟踪。这种异常不会经常发生,并且很难复制。但它通常发生在从其IIS进程空闲回来后。(先运行它,暂时不做任何事情,然后访问与Lucene.Net搜索有关的页面)。我对 IIS 的东西不太有经验。如果有人能帮助我解决这个问题,将会非常有帮助~。谢谢你
Exception: System.NullReferenceException: Object reference not set to an instance of an object.
at J2N.Collections.Generic.HashSet`1.AddIfNotPresent(T value)
at Lucene.Net.Index.IndexReader.SubscribeToGetCacheKeysEvent(GetCacheKeysEvent getCacheKeysEvent)
at Lucene.Net.Search.FieldCacheImpl.Cache`2.Get(AtomicReader reader, TKey key, Boolean setDocsWithField)
at Lucene.Net.Search.FieldCacheImpl.GetInt64s(AtomicReader reader, String field, IInt64Parser parser, Boolean setDocsWithField)
at Lucene.Net.Search.FieldComparer.Int64Comparer.SetNextReader(AtomicReaderContext context)
at Lucene.Net.Search.TopFieldCollector.OneComparerNonScoringCollector.SetNextReader(AtomicReaderContext context)
at Lucene.Net.Search.IndexSearcher.Search(IList`1 leaves, Weight weight, ICollector collector)
调用lucene api的代码为:
var sectionResults = await Task.WhenAll(sectionsData
.Select(data => lucene.ExploreQuery(data.Sorting, data.PageTypeFilter, null, data.CategoryId, null, null, null, null, data.Keyword))
.Select(f => lucene.RetrieveResults(1, 4, f.Sort, null, f.Filter)));
我怀疑是否应该增加应用程序池空闲超时,以减少其发生频率。 https://catchsoftware.com/knowledge-base/application-pool-timeouts/
我检查了 AddIfNotPresent 方法,但不知道运行时哪个引用可能为空。即使我知道它在哪里,我也无法更改它,因为它是一个图书馆。
private bool AddIfNotPresent(T value)
{
if (_buckets == null)
{
Initialize(0);
}
int hashCode;
int bucket;
int collisionCount = 0;
Slot[] slots = _slots;
IEqualityComparer<T>? comparer = _comparer;
if (comparer == null)
{
hashCode = value == null ? 0 : InternalGetHashCode(value.GetHashCode());
bucket = hashCode % _buckets!.Length;
if (default(T)! != null) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
{
for (int i = _buckets[bucket] - 1; i >= 0; i = slots[i].next)
{
if (slots[i].hashCode == hashCode && EqualityComparer<T>.Default.Equals(slots[i].value, value))
{
return false;
}
if (collisionCount >= slots.Length)
{
// The chain of entries forms a loop, which means a concurrent update has happened.
throw new InvalidOperationException(SR.InvalidOperation_ConcurrentOperationsNotSupported);
}
collisionCount++;
}
}
else
{
// Object type: Shared Generic, EqualityComparer<TValue>.Default won't devirtualize
// https://github.com/dotnet/coreclr/issues/17273
// So cache in a local rather than get EqualityComparer per loop iteration
IEqualityComparer<T> defaultComparer = EqualityComparer<T>.Default;
for (int i = _buckets[bucket] - 1; i >= 0; i = slots[i].next)
{
if (slots[i].hashCode == hashCode && defaultComparer.Equals(slots[i].value, value))
{
return false;
}
if (collisionCount >= slots.Length)
{
// The chain of entries forms a loop, which means a concurrent update has happened.
throw new InvalidOperationException(SR.InvalidOperation_ConcurrentOperationsNotSupported);
}
collisionCount++;
}
}
}
else
{
hashCode = value == null ? 0 : InternalGetHashCode(comparer.GetHashCode(value));
bucket = hashCode % _buckets!.Length;
for (int i = _buckets[bucket] - 1; i >= 0; i = slots[i].next)
{
if (slots[i].hashCode == hashCode && comparer.Equals(slots[i].value, value))
{
return false;
}
if (collisionCount >= slots.Length)
{
// The chain of entries forms a loop, which means a concurrent update has happened.
throw new InvalidOperationException(SR.InvalidOperation_ConcurrentOperationsNotSupported);
}
collisionCount++;
}
}
int index;
if (_freeList >= 0)
{
index = _freeList;
_freeList = slots[index].next;
}
else
{
if (_lastIndex == slots.Length)
{
IncreaseCapacity();
// this will change during resize
slots = _slots;
bucket = hashCode % _buckets.Length;
}
index = _lastIndex;
_lastIndex++;
}
slots[index].hashCode = hashCode;
slots[index].value = value;
slots[index].next = _buckets[bucket] - 1;
_buckets[bucket] = index + 1;
_count++;
_version++;
return true;
}
更新:蒂姆·施梅尔特的建议非常有帮助。 Lucene.Net中的以下代码显示了线程不安全的实例变量 getCacheKeysEvents 可能会在多线程上下文中导致异常。
[ExcludeFromRamUsageEstimation]
private readonly ISet<WeakEvents.GetCacheKeysEvent> getCacheKeysEvents = new JCG.HashSet<WeakEvents.GetCacheKeysEvent>();
internal void SubscribeToGetParentReadersEvent(WeakEvents.GetParentReadersEvent getParentReadersEvent)
{
if (getParentReadersEvent is null)
throw new ArgumentNullException(nameof(getParentReadersEvent));
if (getParentReadersEvents.Add(getParentReadersEvent))
getParentReadersEvent.Subscribe(OnGetParentReaders);
}
internal void SubscribeToGetCacheKeysEvent(WeakEvents.GetCacheKeysEvent getCacheKeysEvent)
{
if (getCacheKeysEvent is null)
throw new ArgumentNullException(nameof(getCacheKeysEvent));
if (getCacheKeysEvents.Add(getCacheKeysEvent))
getCacheKeysEvent.Subscribe(OnGetCacheKeys);
}
再次感谢@TimSchmelter 的建设性建议。我最终通过将调用lucene引擎的代码从异步方式更正为同步方式解决了这个问题。新版本已经运行1周多了,问题没有再出现。
var searchTasks = sectionsData.Select(data => lucene.ExploreQuery(data.Sorting, data.PageTypeFilter, null, data.CategoryId, null, null, null, null, data.Keyword))
.Select(f => lucene.RetrieveResults(1, 4, f.Sort, null, f.Filter));
var sectionResults = new J2NGeneric.List<ServiceResponse>();
foreach (var task in searchTasks)
{
sectionResults.Add(await task);
}