开发者

Locking with Constructors causing Null Reference?

So I encountered a strange issue today - I had a simple creation of an instance inside the critical section of a lock, and it would throw a null reference exception when I manually dragged the next line to execute. To illustrate:

public class SearchEngineOptimizationParser
{
    protected static ConcurrentDictionary<string, SearchEngineOptimizationInfo> _referralInformation = null;
    protected static DateTime _lastRecordingDate;
    protected static object _lockRecordingObject = new object();

    protected static Dictionary<string, string> _searchProviderLookups = null;

    static SearchEngineOptimizationParser()
    {
        _referralInformation = new ConcurrentDictionary<string, SearchEngineOptimizationInfo>();
        _lastRecordingDate = DateTime.Now;

        _searchProviderLookups = new Dictionary<string, string>();
        _searchProviderLookups.Add("google.com", "q");
        _searchProviderLookups.Add("yahoo.com", "p");
        _searchProviderLookups.Add("bing.com", "q");
    }

    public SearchEngineOptimizationParser()
    {

    }

    public virtual void ParseReferrer(Uri requestUrl, NameValueCollection serverVariables, ISession session)
    {
        string corePath = requestUrl.PathAndQuery.SmartSplit('?')[0].ToLower();

        string referrer = serverVariables["HTTP_REFERER"];

        if (!string.IsNullOrWhiteSpace(referrer))
        {
            NameValueCollection queryString = HttpUtility.ParseQueryString(referrer);

            string dictionaryKey = session.AffiliateID + "|" + corePath;

            foreach (var searchProvider in _searchProviderLookups)
            {
                if (referrer.Contains(searchProvider.Key))
                {
                    if (queryString[searchProvider.Value] != null)
                    {
                        string keywords = queryString[searchProvider.Value];

                        SearchEngineOptimizationInfo info = new SearchEngineOptimizationInfo
                        {
                            Count = 1,
                            CorePath = corePath,
                            AffiliateId = session.AffiliateID,
                            Keywords = keywords
                        };

   开发者_C百科                     _referralInformation.AddOrUpdate(dictionaryKey, info, (key, oldValue) =>
                        {
                            oldValue.Count++;
                            return oldValue;
                        });

                        break;
                    }
                }
            }
        }

        if (DateTime.Now > _lastRecordingDate.AddHours(1))
        {
            lock (_lockRecordingObject)
            {
                if (DateTime.Now > _lastRecordingDate.AddHours(1))
                {
                    SearchEngineKeywordRepository repository = new SearchEngineKeywordRepository();

                    List<KeyValuePair<string, SearchEngineOptimizationInfo>> currentInfo = _referralInformation.ToList();

                    Action logData = () =>
                    {
                        foreach (var item in currentInfo)
                            repository.LogKeyword(item.Value);
                    };

                    Thread logThread = new Thread(new ThreadStart(logData));
                    logThread.Start();

                    _lastRecordingDate = DateTime.Now;
                    _referralInformation.Clear();
                }
            }
        }
    }

EDIT: Updated Real Object

public class SearchEngineKeywordRepository
{
    public virtual void LogKeyword(SearchEngineOptimizationInfo keywordInfo)
    {
        LogSearchEngineKeywords procedure = new LogSearchEngineKeywords();
        procedure.Execute(keywordInfo.CorePath, keywordInfo.AffiliateId, keywordInfo.Keywords, keywordInfo.Count);
    }
}

The general pattern being that I want to do this 'something' only every hour (in the context of a website application that gets a lot of traffic). I would breakpoint my first if statement, and then step the next line to execute inside the second if statement. When doing so, the act of initializing the SomeObject instance would cause a null reference exception. It had a completely 100% default constructor - I didn't even specify one.

However, when I let the code go through naturally, it would execute without problem. For some reason, it seems that when I skipped over the lock call into the critical section to just test run that code, it caused all kinds of errors.

I'm curious to know why that is; I understand the lock keyword is just syntactic sugar for a Monitor.Enter(o) try / finally block, but that seems to be that when invoking the constructor, something else was happening.

Anyone have any ideas?

EDIT: I've added the actual code to this. I'm able to reproduce this at will, but I still don't understand why this is happening. I've tried copying this code to another solution and the problem does not seem to occur.


I've tried to reproduce your situation, but as I expected I could not. I've tried both the 2.0 and 4.0 runtime, in 32 and 64 bit mode (debugging sometimes behaves differently under x64).

Is the code shown a simplification? Have you checked all your assumptions? I understand you're skipping 3 lines of code, both the if statements and the lock? In that case, even setting the lock object to null does not cause the exception you describe.

(Having _lockRecordingObject set to null causes an ArgumentNullException when leaving the lock scope)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜