Периодически выполняемое задание на уровне библиотеки
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 04.05.20 07:43
Оценка:
Дано: Library, FW3.5-4.8/STD 2.0 и нулевой опыт многопоточного программирования под .NET

Решил добавить в неё кэш с ранее созданными объектами.

Если быть точнее, то это словарь [connectionString]->[объект connectionOptions].

И хочется прикрутить к этому словарю фоновую зачистку устаревших элементов.

Схема проста.

Поток пользователя
Блокирует словарь
{
 Запускает сборщик мусора (или не даем ему завершиться)
 Добавляет в словарь
}


Сборщик мусора
for(;;)
{
 Блокирует словарь
 {
  Удаляет устаревшие элементы

  Если словарь стал пустым, то завершаем работу
 }
 
 Sleep(1 сек)
}


Вопрос насчет выбора механизма для сборщика мусора.

Основные опасения — как этот фоновый сборщик мусора будет себя вести при завершении работы?

--- 1.
Сразу подумал про Thread+Sleep. Но решил пока не с ним связываться. Наверное это слишком дубовый способ.

--- 2.
Замутил тестовое приложение с использованием Timer.

Основная проблема — метод сборщика мусора может вызваться до завершения предыдущего вызова. Не очень красиво.

  Если интересно, то вот тестовый код
using System;
using System.Threading;

namespace ConsoleApp1{
////////////////////////////////////////////////////////////////////////////////
//class Program

class Program
{
 static object sm_LogGuard=new object(); 

 static object sm_Resource__Guard=new object(); 

 static int sm_Resource__Counter=0;

 static System.Threading.Timer sm_Resource__GC_Timer=null;

 //-------------------------------------------------
 static void Main(string[] args)
 {
  const string c_src="main";

  const int c_counter_init=9;

  for(int n0=0;n0!=10;++n0)
  {
   for(int n=0;n!=4;++n)
   {
    Helper__Log(c_src,"------------------ n: {0}",n);

    lock(sm_Resource__Guard)
    {
     if(sm_Resource__GC_Timer==null)
     {
      Helper__Log(c_src,"setup timer");
   
      sm_Resource__GC_Timer=new Timer(Resource_GC,null,0,1*1000);
     }
   
     if(sm_Resource__Counter==0)
     {
      Helper__Log(c_src,"set to {0}",c_counter_init);
   
      Interlocked.Exchange(ref sm_Resource__Counter,c_counter_init);
     }
     else
     {
      int x=Interlocked.Increment(ref sm_Resource__Counter);
   
      Helper__Log(c_src,"increment to {0}",x);
     }//else
    }//lock
   
    Thread.Sleep(2*1000);
   }//for n

   Helper__Log(c_src,"RESET");

   var t=Interlocked.Exchange(ref sm_Resource__GC_Timer,null);
    
   if(t!=null)
    t.Dispose();
  }//for[ever]

  Helper__Log(c_src,"EXIT");
 }//main

 //----------------------------------------------------------------------
 static void Resource_GC(object a)
 {
  string c_src=string.Format("GC_{0}",Thread.CurrentThread.ManagedThreadId);

  for(uint n=0;;)
  {
   ++n;

   lock(sm_Resource__Guard)
   {
    if(sm_Resource__Counter==0)
    {
     var t=Interlocked.Exchange(ref sm_Resource__GC_Timer,null);
    
     if(t==null)
     {
      Helper__Log(c_src,"Stop [ACHTUNG]!");
     }
     else
     {
      Helper__Log(c_src,"Stop GC");
    
      t.Dispose();
     }//if
    
     return;
    }
    
    if(n>3)
    {
     Helper__Log(c_src,"Exit");
     return;
    }
    
    var c=Interlocked.Decrement(ref sm_Resource__Counter);
    
    Helper__Log(c_src,"decrement to {0}",c);
   }//lock
  }//for[ever]
 }//Resource_GC

 //----------------------------------------------------------------------
 static void Helper__Log(string src,string format,params object[] p)
 {
  string msg=string.Format(format,p);

  lock(sm_LogGuard)
  {
   Console.WriteLine("[{0}][Thr {1}] {2}",DateTime.Now,src,msg);
  }
 }//Helper__Log
}//class Program

////////////////////////////////////////////////////////////////////////////////
}

--- 3.
"Слышал", что есть такая штука как Task....

-------------
Кто-нибуль может показать простой/надежный/правильный код для этой задачи?

Или не париться и заюзать Thread+Sleep?
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: Периодически выполняемое задание на уровне библиотеки
От: Sharov Россия  
Дата: 04.05.20 20:13
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

Timer лучше всего для данной ситуации, не надо отъедать поток у CLR.

КД>Основная проблема — метод сборщика мусора может вызваться до завершения предыдущего вызова. Не очень красиво.


Проверять какой-нибудь флаг либо что-то вроде double-checked locking -- если кто-то есть в критической секции, завершаем выполнение (return).
Кодом людям нужно помогать!
Re: Периодически выполняемое задание на уровне библиотеки
От: VladCore  
Дата: 04.05.20 20:33
Оценка: 10 (1)
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Дано: Library, FW3.5-4.8/STD 2.0 и нулевой опыт многопоточного программирования под .NET


КД>Решил добавить в неё кэш с ранее созданными объектами.


КД>Если быть точнее, то это словарь [connectionString]->[объект connectionOptions].


а что там в [объект connectionOptions] если не секрет?


КД>И хочется прикрутить к этому словарю фоновую зачистку устаревших элементов.


КД>Схема проста.


Уже реализовано и HttpRuntime.Cache (net 3.5 и младше) и в MemoryCache (Net 4.0 и старше)

настройки устаревания можно задавать или Absolute date или Sliding interval

Работает очень быстро, т.к. устаревший объекты удаляются фоновым сборщиком мусора.
Re[2]: Периодически выполняемое задание на уровне библиотеки
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 05.05.20 04:43
Оценка:
Здравствуйте, VladCore, Вы писали:

КД>>Решил добавить в неё кэш с ранее созданными объектами.


КД>>Если быть точнее, то это словарь [connectionString]->[объект connectionOptions].


VC>а что там в [объект connectionOptions] если не секрет?


Разобранная connectionString

Core_ConnectionOptions из вот этой штуки

  Вот оно, обновленное (осторожно, размер!)
////////////////////////////////////////////////////////////////////////////////
//LCPI Data OleDb. Core. class Core_ConnectionOptions.
//                                                 Kovalenko Dmitry. 23.09.2011.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;

using structure_lib=lcpi.lib.structure;
using com_lib=lcpi.lib.com;
using oledb_lib=lcpi.lib.oledb;

namespace lcpi.data.oledb.core{
////////////////////////////////////////////////////////////////////////////////

sealed class Core_ConnectionOptions
{
 public const string c_sys_prop_name__Provider
  ="Provider";

 public const string c_sys_prop_name__FileName
  ="File Name";

 public const string c_sys_prop_name__NestedTransRules
  ="NetProv: NestedTransRules";
 
 //-----------------------------------------------------------------------
 public const string c_prov_prop_name__DataSource
  ="Data Source";

 public const string c_prov_prop_name__InitCatalog
  ="Initial Catalog";

 public const string c_prov_prop_name__Location
  ="Location";

 public const string c_prov_prop_name__PersistSecurityInfo
  ="Persist Security Info";

 public const string c_prov_prop_name__UserID
  ="User ID";

 public const string c_prov_prop_name__Password
  ="Password";

 public const string c_prov_prop_name__IntegratedSecurity
  ="Integrated Security";

 public const string c_prov_prop_name__OleDbServices
  ="OLE DB Services";

 //-----------------------------------------------------------------------
 private const string c_prov_prop_name__Pswd
  ="Pswd";

 private const string c_prov_prop_name__Pwd
  ="Pwd";

 //-----------------------------------------------------------------------
 public const string c_ibp_prop_name__role
  ="role";

 public const string c_ibp_prop_name__ctype
  ="ctype";

 public const string c_ibp_prop_name__ctype_none
  ="ctype_none";

 public const string c_ibp_prop_name__auto_commit
  ="auto_commit";

 public const string c_ibp_prop_name__dbclient_type
  ="dbclient_type";

 public const string c_ibp_prop_name__dbclient_library
  ="dbclient_library";

 public const string c_ibp_prop_name__dbclient_library_64
  ="dbclient_library_64";

 public const string c_ibp_prop_name__icu_library
  ="icu_library";

 public const string c_ibp_prop_name__icu_library_64
  ="icu_library_64";

 public const string c_ibp_prop_name__named_param_prefix
  ="named_param_prefix";

 public const string c_ibp_prop_name__nested_trans
  ="nested_trans";

 //-----------------------------------------------------------------------
 public const OleDbPropertiesValues.NetProvider.NestedTransRules
  c_sys_prop_def_value__NestedTransRules
   =OleDbPropertiesValues.NetProvider.NestedTransRules.None;

 public const oledb_lib.DBPROPVAL__OLEDBSERVICES
  c_prov_prop_def_value__OleDbServices
   =oledb_lib.DBPROPVAL__OLEDBSERVICES.ENABLEALL
     &(~(oledb_lib.DBPROPVAL__OLEDBSERVICES.AGR_AFTERSESSION|
         oledb_lib.DBPROPVAL__OLEDBSERVICES.CLIENTCURSOR));

 public const bool
  c_prov_prop_def_value__PersistSecurityInfo
   =false; 

 //public typedefs -------------------------------------------------------
 public class tagPropertyData
 {
  protected tagPropertyData(string name,string value)
  {
   m_name  =name;
   m_value =value;
  }

  //properties -----------------------------
  public string name
  {
   get
   {
    return m_name;
   }
  }//name

  public string value
  {
   get
   {
    return m_value;
   }
  }//value

  //----------------------------------------
  protected readonly string m_name;
  protected string          m_value;
 };//class tagPropertyData

 //constructors ----------------------------------------------------------
 private Core_ConnectionOptions(string connectionString)
 {  
  Debug.Assert(Object.ReferenceEquals(m_provider_props,null));
  Debug.Assert(!Object.ReferenceEquals(connectionString,null));

  //----------------------------------------
  m_ConnectionString=connectionString;

  if(string.IsNullOrEmpty(m_ConnectionString))
   return;
  
  //----------------------------------------
  Debug.Assert(!string.IsNullOrEmpty(m_ConnectionString));

  m_provider_props=new List<tagPropertyDataRW>();

  //----
  {
   var CnsElements=Helper__BuildParamList(m_ConnectionString);

   for(int i=0,_c=CnsElements.Size();i!=_c;++i)
   {
    var CnsElement=CnsElements[i];

    Debug.Assert(!Object.ReferenceEquals(CnsElement,null));
    Debug.Assert(!Object.ReferenceEquals(CnsElement.Name,null));

    if(this.Load__InstallSysProp(CnsElement.Name,CnsElement.Value))
     continue;

    this.Load__InstallProviderProp(CnsElement.Name,CnsElement.Value);
   }//for i
  }//local

  //---------------------------------------- REG "OLE DB Services"
  {
   var x=this.Helper__FindProviderPropertyByName(c_prov_prop_name__OleDbServices);

   if(Object.ReferenceEquals(x,null))
   {
    string strValue
     =((Int32)c_prov_prop_def_value__OleDbServices).ToString();

    this.Load__InstallProviderProp(c_prov_prop_name__OleDbServices,
                                   strValue);

    m_prov_prop__OleDbServices=c_prov_prop_def_value__OleDbServices;
   }//if
   else
   {
    m_prov_prop__OleDbServices=
     (oledb_lib.DBPROPVAL__OLEDBSERVICES)Helper__ConvertPropValueToInt32
       (c_prov_prop_name__OleDbServices,
        x.value,
        (Int32)c_prov_prop_def_value__OleDbServices);

    x.value=((Int32)m_prov_prop__OleDbServices).ToString();
   }//else
  }//local - REG "OLE DB Services"

  //---------------------------------------- REG "Persist Security Info"
  {
   var x=this.Helper__FindProviderPropertyByName(c_prov_prop_name__PersistSecurityInfo);

   if(Object.ReferenceEquals(x,null))
   {
    string strValue
     =dbprops_cvts.utils.Core_DBPropCvt__Utils__Boolean_to_String.Exec
       (c_prov_prop_def_value__PersistSecurityInfo);

    this.Load__InstallProviderProp
     (c_prov_prop_name__PersistSecurityInfo,
      strValue);

    m_prov_prop__PersistSecurityInfo=c_prov_prop_def_value__PersistSecurityInfo;
   }//if
   else
   {
    m_prov_prop__PersistSecurityInfo
     =Helper__ConvertPropValueToBool
       (c_prov_prop_name__PersistSecurityInfo,
        x.value,
        c_prov_prop_def_value__PersistSecurityInfo);

    x.value
     =dbprops_cvts.utils.Core_DBPropCvt__Utils__Boolean_to_String.Exec
       (m_prov_prop__PersistSecurityInfo);
   }//else
  }//local - REG "Persist Security Info"
 }//Core_ConnectionOptions

 //-----------------------------------------------------------------------
 public static Core_ConnectionOptions Create(string connectionString)
 {
  Debug.Assert(!Object.ReferenceEquals(connectionString,null));

  //---------------------------------------- 1.
  Debug.Assert(!Object.ReferenceEquals(sm_Cache__Dictionary,null));

  lock(sm_Cache__Dictionary)
  {
   Core_ConnectionOptions x=null;

   if(sm_Cache__Dictionary.TryGetValue(connectionString,out x))
   {
    Debug.Assert(!Object.ReferenceEquals(x,null));

    Helper__CacheList__BringToHead(x);

    return x;
   }//if

   Debug.Assert(Object.ReferenceEquals(x,null));
  }//lock sm_Cache__Dictionary

  //---------------------------------------- 2.
  var newObject=new Core_ConnectionOptions(connectionString);

  //---------------------------------------- 3.
  lock(sm_Cache__Dictionary)
  {
   Core_ConnectionOptions x=null;

   if(sm_Cache__Dictionary.TryGetValue(connectionString,out x))
   {
    Debug.Assert(!Object.ReferenceEquals(x,null));

    Helper__CacheList__BringToHead(x);

    return x;
   }//if

   Debug.Assert(Object.ReferenceEquals(x,null));

   Helper__Cache__Add(newObject);

   while(sm_Cache__Dictionary.Count>Core_Config__Static.ConnectionOptionsCache__MaxSize)
   {
    Debug.Assert(!Object.ReferenceEquals(sm_CacheList__Tail,null));

    Helper__Cache__Delete(sm_CacheList__Tail);
   }
  }//lock sm_Cache__Dictionary

  //---------------------------------------- 4.
  return newObject;
 }//Create

 //properties ------------------------------------------------------------
 public string ConnectionString
 {
  get
  {
   return m_ConnectionString;
  }
 }//ConnectionString

 //-----------------------------------------------------------------------
 public string Provider
 {
  get
  {
   return m_sys_prop__Provider;
  }
 }//Provider

 //-----------------------------------------------------------------------
 public string FileName
 {
  get
  {
   return m_sys_prop__FileName;
  }
 }//FileName

 //-----------------------------------------------------------------------
 //! \note
 //!  Used in tests only.
 public bool PersistSecurityInfo
 {
  get
  {
   return m_prov_prop__PersistSecurityInfo;
  }//get
 }//PersistSecurityInfo
 
 //-----------------------------------------------------------------------
 public OleDbPropertiesValues.NetProvider.NestedTransRules NetProvider_NestedTransRules
 {
  get
  {
   return m_sys_prop__NestedTransRules;
  }
 }//NetProvider_NestedTransRules
 
 //-----------------------------------------------------------------------
 public string DataSource
 {
  get
  {
   return this.Helper__GetProviderPropertyValue__AsString(c_prov_prop_name__DataSource);
  }
 }//DataSource

 //-----------------------------------------------------------------------
 public string InitCatalog
 {
  get
  {
   return this.Helper__GetProviderPropertyValue__AsString(c_prov_prop_name__InitCatalog);
  }
 }//InitialCatalog

 //-----------------------------------------------------------------------
 public oledb_lib.DBPROPVAL__OLEDBSERVICES OleDbServices
 {
  get
  {
   return m_prov_prop__OleDbServices;
  }
 }//OleDbServices

 //-----------------------------------------------------------------------
 public bool Enlist
 {
  get
  {
   return (m_prov_prop__OleDbServices&oledb_lib.DBPROPVAL__OLEDBSERVICES.TXNENLISTMENT)
            ==oledb_lib.DBPROPVAL__OLEDBSERVICES.TXNENLISTMENT;
  }
 }//Enlist

 //interface -------------------------------------------------------------
 public static bool IsNetProviderProperty(string name)
 {
  Debug.Assert(!Object.ReferenceEquals(name,null));

  Debug.Assert(!Object.ReferenceEquals(sm_net_provider_properties,null));

  foreach(var x in sm_net_provider_properties)
  {
   Debug.Assert(!Object.ReferenceEquals(x,null));
   Debug.Assert(x.Length>0);
   Debug.Assert(x.Trim()==x);

   if(sm_provider_props_comparer.Compare(x,name)==0)
    return true;
  }//foreach x

  return false;
 }//IsNetProviderProperty

 //-----------------------------------------------------------------------
 public static bool IsSystemProperty(string name)
 {
  Debug.Assert(!Object.ReferenceEquals(name,null));

  Debug.Assert(!Object.ReferenceEquals(sm_system_properties,null));

  foreach(var x in sm_system_properties)
  {
   Debug.Assert(!Object.ReferenceEquals(x,null));
   Debug.Assert(x.Length>0);
   Debug.Assert(x.Trim()==x);

   if(sm_provider_props_comparer.Compare(x,name)==0)
    return true;
  }//foreach x

  return false;
 }//IsSystemProperty

 //-----------------------------------------------------------------------
 public string GetUserConnectionString(bool HidePassword)
 {
  Debug.Assert(!Object.ReferenceEquals(m_ConnectionString,null));

  if(m_prov_prop__PersistSecurityInfo)
   HidePassword=false;

  if(!HidePassword)
   return m_ConnectionString;

  //----
  Debug.Assert(HidePassword);

  var CnsElements=Helper__BuildParamList(m_ConnectionString);

  for(int i=CnsElements.Size();i!=0;)
  {
   --i;

   var CnsElement=CnsElements[i];

   Debug.Assert(!Object.ReferenceEquals(CnsElement,null));
   Debug.Assert(!Object.ReferenceEquals(CnsElement.Name,null));

   if(!Helper__IsPasswordProperty(CnsElement.Name))
    continue;

   CnsElements.Erase(i);
  }//for i

  return CnsElements.CreateText();
 }//GetUserConnectionString

 //-----------------------------------------------------------------------
 public int GetProviderPropertyCount()
 {
  if(Object.ReferenceEquals(m_provider_props,null))
   return 0;

  return m_provider_props.Count;
 }//GetPropertyCount

 //-----------------------------------------------------------------------
 public tagPropertyData GetProviderProperty(int index)
 {
  Debug.Assert(!Object.ReferenceEquals(m_provider_props,null));
  Debug.Assert(index>=0);
  Debug.Assert(index<m_provider_props.Count);
 
  return m_provider_props[index];
 }//GetProviderProperty

 //private types ---------------------------------------------------------
 private sealed class tagPropertyDataRW:tagPropertyData
 {
  public tagPropertyDataRW(string name,
                           string value)
   :base(name,value)
  {;}

  public new string value
  {
   get
   {
    return base.m_value;
   }

   set
   {
    base.m_value=value;
   }
  }//value
 };//class tagPropertyDataRW

 //-----------------------------------------------------------------------
 private sealed class tagPropertyData_CompareByName
  :structure_lib.IComparer2<tagPropertyDataRW,string>
 {
  public int Compare(tagPropertyDataRW data1,
                     tagPropertyDataRW data2) //abstract
  {
   Debug.Assert(!Object.ReferenceEquals(data1,null));
   Debug.Assert(!Object.ReferenceEquals(data2,null));

   return this.Compare(data1.name,data2.name);
  }//Compare - data1, data2

  //----------------------------------------
  public int Compare(tagPropertyDataRW data,
                     string            name) //abstract
  {
   Debug.Assert(!Object.ReferenceEquals(data,null));

   return this.Compare(data.name,name);
  }//Compare - data, name

  //----------------------------------------
  public int Compare(string            name,
                     tagPropertyDataRW data) //abstract
  {
   Debug.Assert(!Object.ReferenceEquals(data,null));

   return this.Compare(name,data.name);
  }//Compare - name, data

  //----------------------------------------
  public int Compare(string name1,string name2)
  {
   Debug.Assert(!Object.ReferenceEquals(name1,null));
   Debug.Assert(!Object.ReferenceEquals(name2,null));

   return sm_cmp.Compare(name1,name2);
  }//Compare - name1, name2

  //----------------------------------------------------------------------
  private static readonly StringComparer
   sm_cmp=System.StringComparer.OrdinalIgnoreCase;
 };//class tagPropertyData_CompareByName

 //private methods -------------------------------------------------------
 private bool Load__InstallSysProp(string name,string value)
 {
  Debug.Assert(!Object.ReferenceEquals(name,null));

  if(Object.ReferenceEquals(name,null))
   return false;

  var comparer=System.StringComparer.OrdinalIgnoreCase;

  if(comparer.Compare(name,c_sys_prop_name__Provider)==0)
   this.Load__InstallSysProp__Provider(value);
  else
  if(comparer.Compare(name,c_sys_prop_name__FileName)==0)
   this.Load__InstallSysProp__FileName(value);
  else
  if(comparer.Compare(name,c_sys_prop_name__NestedTransRules)==0)
   this.Load__InstallSysProp__NestedTransRules(value);
  else
   return false;

  return true;
 }//Load__InstallSysProp

 //-----------------------------------------------------------------------
 private void Load__InstallSysProp__Provider(string value)
 {
  m_sys_prop__Provider=value;
 }//Load__InstallSysProp__Provider

 //-----------------------------------------------------------------------
 private void Load__InstallSysProp__FileName(string value)
 {
  m_sys_prop__FileName=value;
 }//Load__InstallSysProp__FileName

 //-----------------------------------------------------------------------
 private void Load__InstallSysProp__NestedTransRules(string value)
 {
  try
  {
   m_sys_prop__NestedTransRules
    =dbprops_svcs.NetProvider.Core_DBPropSvc__NetProvider__NestedTransRules.ConvertValue(value);
  }
  catch(Exception e)
  {
   Helper__ThrowArgError__InvalidCnPropertyValue(c_sys_prop_name__NestedTransRules,e);
  }//catch
 }//Load__InstallSysProp__NestedTransRules
 
 //-----------------------------------------------------------------------
 private void Load__InstallProviderProp(string name,string value)
 {
  Debug.Assert(!Object.ReferenceEquals(name,null));
  Debug.Assert(!Object.ReferenceEquals(m_provider_props,null));
  Debug.Assert(!Object.ReferenceEquals(sm_provider_props_comparer,null));
 
  var x=structure_lib.LowerSearch.Exec(m_provider_props,
                                       name,
                                       sm_provider_props_comparer);
  if(!x.result)
  {
   Debug.Assert(x.position<=m_provider_props.Count);

   m_provider_props.Insert(x.position,new tagPropertyDataRW(name,value));
  }
  else
  {
   //[2012-05-20] This is unexpected mode for this method
   Debug.Assert(false);
   Debug.Assert(x.position<m_provider_props.Count);
   Debug.Assert(!Object.ReferenceEquals(m_provider_props[x.position],null));
  }//else
 }//Load__InstallProviderProp

 //-----------------------------------------------------------------------
 private string Helper__GetProviderPropertyValue__AsString(string name)
 {
  Debug.Assert(!Object.ReferenceEquals(name,null));

  var PropertyData=this.Helper__FindProviderPropertyByName(name);

  if(Object.ReferenceEquals(PropertyData,null))
   return null;

  return PropertyData.value;
 }//Helper__GetProviderPropertyValue__AsString

 //-----------------------------------------------------------------------
 private tagPropertyDataRW Helper__FindProviderPropertyByName(string name)
 {
  Debug.Assert(!Object.ReferenceEquals(name,null));

  if(Object.ReferenceEquals(m_provider_props,null))
   return null;

  Debug.Assert(!Object.ReferenceEquals(m_provider_props,null));
  Debug.Assert(!Object.ReferenceEquals(sm_provider_props_comparer,null));

  var x=structure_lib.LowerSearch.Exec(m_provider_props,
                                       name,
                                       sm_provider_props_comparer);
  if(!x.result)
   return null;

  Debug.Assert(x.position>=0);
  Debug.Assert(x.position<m_provider_props.Count);

  var resultValue=m_provider_props[x.position];

  Debug.Assert(!Object.ReferenceEquals(resultValue,null));

  return resultValue;
 }//Helper__FindProviderPropertyByName

 //-----------------------------------------------------------------------
 private static Int32 Helper__ConvertPropValueToInt32(string propName,
                                                      string strValue,
                                                      Int32  defValue)
 {
  Debug.Assert(!Object.ReferenceEquals(propName,null));

  Int32 resultValue=defValue;

  try
  {
   var x=dbprops_cvts.utils.Core_DBPropCvt__Utils__String_to_Int32.Exec(strValue);

   if(x.HasValue)
    resultValue=x.Value;
  }
  catch(FormatException e1)
  {
   Helper__ThrowArgError__InvalidCnPropertyValue(propName,e1);
  }//catch
  catch(OverflowException e2)
  {
   Helper__ThrowArgError__InvalidCnPropertyValue(propName,e2);
  }//catch

  return resultValue;
 }//Helper__ConvertPropValueToInt32

 //-----------------------------------------------------------------------
 private static bool Helper__ConvertPropValueToBool(string propName,
                                                    string strValue,
                                                    bool   defValue)
 {
  Debug.Assert(!Object.ReferenceEquals(propName,null));

  bool resultValue=defValue;

  try
  {
   var x=dbprops_cvts.utils.Core_DBPropCvt__Utils__String_to_Boolean.Exec(strValue);//throw

   if(x.HasValue)
    resultValue=x.Value;
  }
  catch(FormatException e1)
  {
   Helper__ThrowArgError__InvalidCnPropertyValue(propName,e1);
  }//catch

  return resultValue;
 }//Helper__ConvertPropValueToBool

 //-----------------------------------------------------------------------
 private static bool Helper__IsPasswordProperty(string name)
 {
  Debug.Assert(!Object.ReferenceEquals(name,null));

  var comparer=System.StringComparer.OrdinalIgnoreCase;

  Debug.Assert(!Object.ReferenceEquals(comparer,null));

  if(comparer.Compare(name,c_prov_prop_name__Password)==0)
   return true;

  if(comparer.Compare(name,c_prov_prop_name__Pswd)==0)
   return true;

  if(comparer.Compare(name,c_prov_prop_name__Pwd)==0)
   return true;

  if(comparer.Compare(name,"sys_encrypt_password")==0)
   return true;

  if(comparer.Compare(name,"sep")==0)
   return true;

  return false;
 }//Helper__IsPasswordProperty

 //-----------------------------------------------------------------------
 private static structure_lib.ParameterList Helper__BuildParamList(string ConnectionString)
 {
  Debug.Assert(!Object.ReferenceEquals(ConnectionString,null));

  return new structure_lib.ParameterList(ConnectionString);
 }//Helper__BuildParamList

 //-----------------------------------------------------------------------
 private static void Helper__ThrowArgError__InvalidCnPropertyValue
                                           (string     PropertyName,
                                            Exception  innerException)
 {
  Debug.Assert(!Object.ReferenceEquals(PropertyName,null));
  Debug.Assert(!Object.ReferenceEquals(innerException,null));

  var err_rec=new Core_ExceptionRecord
               (com_lib.HResultCode.E_INVALIDARG,
                ErrSourceID.OleDbConnection,
                ErrMessageID.prop_err__invalid_cn_str_prop_value_1);

  err_rec.push(PropertyName);

  ThrowSysError.invalid_arg(err_rec,innerException);
 }//Helper__ThrowArgError__InvalidCnPropertyValue

 //-----------------------------------------------------------------------
 private static void Helper__Cache__Add(Core_ConnectionOptions node)
 {
  Debug.Assert(!Object.ReferenceEquals(node,null));
  Debug.Assert(Object.ReferenceEquals(node.m_List_Prev,null));
  Debug.Assert(Object.ReferenceEquals(node.m_List_Next,null));

  RuntimeHelpers.PrepareConstrainedRegions();        //------------------- CER

  try
  {
  }
  finally
  {
   sm_Cache__Dictionary.Add(node.ConnectionString,node); //throw OOM

   Helper__CacheList__PushFront__CER(node);
  }//finally                                         //------------------- /CER
 }//Helper__Cache__Add

 //-----------------------------------------------------------------------
 private static void Helper__Cache__Delete(Core_ConnectionOptions node)
 {
  Debug.Assert(!Object.ReferenceEquals(node,null));
  Debug.Assert(!Object.ReferenceEquals(sm_CacheList__Head,null));
  Debug.Assert(!Object.ReferenceEquals(sm_CacheList__Tail,null));

  RuntimeHelpers.PrepareConstrainedRegions();        //------------------- CER

  try
  {
  }
  finally
  {
#if DEBUG
   bool r=
#endif
    sm_Cache__Dictionary.Remove(node.ConnectionString);

#if DEBUG
   Debug.Assert(r);
#endif

   Helper__CacheList__Remove__CER(node);
  }//finally                                         //------------------- /CER
 }//Helper__Cache__Add

 //-----------------------------------------------------------------------
 private static void Helper__CacheList__BringToHead(Core_ConnectionOptions node)
 {
  Debug.Assert(!Object.ReferenceEquals(node,null));

  RuntimeHelpers.PrepareConstrainedRegions();       //-------------------- CER

  try
  {
  }
  finally
  {
   if(!Object.ReferenceEquals(node,sm_CacheList__Head))
   {
    Helper__CacheList__Remove__CER(node);
    Helper__CacheList__PushFront__CER(node);
   }//if
  }//finally                                        //-------------------- /CER
 }//Helper__Cache__BringToHead

 //-----------------------------------------------------------------------
 private static void Helper__CacheList__Remove__CER(Core_ConnectionOptions node)
 {
  Debug.Assert(!Object.ReferenceEquals(node,null));
  
  Debug.Assert(!Object.ReferenceEquals(sm_CacheList__Head,null));
  Debug.Assert(!Object.ReferenceEquals(sm_CacheList__Tail,null));

  if(Object.ReferenceEquals(sm_CacheList__Head,node))
  {
   //Remove from HEAD
   Debug.Assert(Object.ReferenceEquals(node.m_List_Prev,null));

   if(Object.ReferenceEquals(sm_CacheList__Tail,node))
   {
    Debug.Assert(Object.ReferenceEquals(node.m_List_Next,null));

    sm_CacheList__Head=null;
    sm_CacheList__Tail=null;
   }
   else
   {
    Debug.Assert(!Object.ReferenceEquals(sm_CacheList__Head,sm_CacheList__Tail));

    var newHead=node.m_List_Next;

    Debug.Assert(Object.ReferenceEquals(newHead.m_List_Prev,node));

    newHead.m_List_Prev=null;

    sm_CacheList__Head=newHead;
   }//else
  }
  else
  if(Object.ReferenceEquals(sm_CacheList__Tail,node))
  {
   //Remove from TAIL      
   Debug.Assert(Object.ReferenceEquals(node.m_List_Next,null));

   Debug.Assert(!Object.ReferenceEquals(sm_CacheList__Head,node));

   var newTail=node.m_List_Prev;

   Debug.Assert(Object.ReferenceEquals(newTail.m_List_Next,node));

   newTail.m_List_Next=null;

   sm_CacheList__Tail=newTail;
  }
  else
  {
   //Remove middle list item
   Debug.Assert(!Object.ReferenceEquals(sm_CacheList__Head,sm_CacheList__Tail));
   Debug.Assert(!Object.ReferenceEquals(sm_CacheList__Head.m_List_Next,sm_CacheList__Tail));
   Debug.Assert(!Object.ReferenceEquals(sm_CacheList__Head,sm_CacheList__Tail.m_List_Prev));

   Debug.Assert(!Object.ReferenceEquals(sm_CacheList__Head,node));
   Debug.Assert(!Object.ReferenceEquals(sm_CacheList__Tail,node));

   Debug.Assert(!Object.ReferenceEquals(node.m_List_Prev,null));
   Debug.Assert(!Object.ReferenceEquals(node.m_List_Next,null));

   Debug.Assert(Object.ReferenceEquals(node.m_List_Prev.m_List_Next,node));
   Debug.Assert(Object.ReferenceEquals(node.m_List_Next.m_List_Prev,node));

   node.m_List_Prev.m_List_Next=node.m_List_Next;
   node.m_List_Next.m_List_Prev=node.m_List_Prev;
  }//else

  node.m_List_Prev=null;
  node.m_List_Next=null;
 }//Helper__CacheList__Remove__CER

 //-----------------------------------------------------------------------
 private static void Helper__CacheList__PushFront__CER(Core_ConnectionOptions node)
 {
  Debug.Assert(!Object.ReferenceEquals(node,null));
  Debug.Assert(Object.ReferenceEquals(node.m_List_Prev,null));
  Debug.Assert(Object.ReferenceEquals(node.m_List_Next,null));

  if(Object.ReferenceEquals(sm_CacheList__Head,null))
  {
   Debug.Assert(Object.ReferenceEquals(sm_CacheList__Tail,null));

   sm_CacheList__Head=node;
   sm_CacheList__Tail=node;

   return;
  }//if

  Debug.Assert(!Object.ReferenceEquals(sm_CacheList__Head,null));
  Debug.Assert(!Object.ReferenceEquals(sm_CacheList__Tail,null));

  Debug.Assert(!Object.ReferenceEquals(sm_CacheList__Head,node));

  node.m_List_Next=sm_CacheList__Head;

  sm_CacheList__Head.m_List_Prev=node;

  sm_CacheList__Head=node;
 }//Helper__CacheList__PushFront__CER

 //private data [cache list] ---------------------------------------------
 Core_ConnectionOptions m_List_Prev=null;
 Core_ConnectionOptions m_List_Next=null;

 //private data ----------------------------------------------------------
 /// <summary>
 ///  Original connection string
 /// </summary>
 private string m_ConnectionString=null;

 private string m_sys_prop__Provider=null;

 private string m_sys_prop__FileName=null;

 private OleDbPropertiesValues.NetProvider.NestedTransRules
  m_sys_prop__NestedTransRules=c_sys_prop_def_value__NestedTransRules;

 private readonly List<tagPropertyDataRW> m_provider_props=null;

 private oledb_lib.DBPROPVAL__OLEDBSERVICES
  m_prov_prop__OleDbServices=c_prov_prop_def_value__OleDbServices;

 private bool
  m_prov_prop__PersistSecurityInfo=c_prov_prop_def_value__PersistSecurityInfo;

 //-----------------------------------------------------------------------
 private static readonly tagPropertyData_CompareByName
  sm_provider_props_comparer=new tagPropertyData_CompareByName();

 private static readonly string[]
  sm_net_provider_properties=
  {
   c_sys_prop_name__NestedTransRules,
  };//sm_net_provider_properties

 private static readonly string[]
  sm_system_properties=
  {
   c_sys_prop_name__Provider,
   c_sys_prop_name__FileName,
   c_sys_prop_name__NestedTransRules,
  };//sm_system_properties

 //-----------------------------------------------------------------------
 private static Dictionary<string,Core_ConnectionOptions> sm_Cache__Dictionary
  =new Dictionary<string,Core_ConnectionOptions>();

 //-----------------------------------------------------------------------
 private static Core_ConnectionOptions sm_CacheList__Head=null;
 private static Core_ConnectionOptions sm_CacheList__Tail=null;
};//class Core_ConnectionOptions
 
////////////////////////////////////////////////////////////////////////////////
}//namespace lcpi.data.oledb.core

КД>>И хочется прикрутить к этому словарю фоновую зачистку устаревших элементов.

Еще пока хочется, но уже не так сильно.

Я сейчас, при добавлении нового элемента, просто удаляю старый, если общее количество элементов превысило лимит.

VC>Уже реализовано и HttpRuntime.Cache (net 3.5 и младше) и в MemoryCache (Net 4.0 и старше)


Посмотрю, спасибо!
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[3]: Периодически выполняемое задание на уровне библиотеки
От: Sinclair Россия https://github.com/evilguest/
Дата: 05.05.20 05:11
Оценка: +1
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Я сейчас, при добавлении нового элемента, просто удаляю старый, если общее количество элементов превысило лимит.

Этот способ прост и понятен. Есть какие-то недостатки, которые вы хотите исправить путём выноса сборщика мусора в отдельный поток?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[4]: Периодически выполняемое задание на уровне библиотеки
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 05.05.20 06:25
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Коваленко Дмитрий, Вы писали:


КД>>Я сейчас, при добавлении нового элемента, просто удаляю старый, если общее количество элементов превысило лимит.

S>Этот способ прост и понятен. Есть какие-то недостатки, которые вы хотите исправить путём выноса сборщика мусора в отдельный поток?

Я "где-то читал", что если объект зависнет надолго, то потом сборщик мусора (GC) перестанет пытаться освобождать из под него память.

И эта мысль меня беспокоит
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[5]: Периодически выполняемое задание на уровне библиотеки
От: Sinclair Россия https://github.com/evilguest/
Дата: 05.05.20 08:15
Оценка: +3
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Я "где-то читал", что если объект зависнет надолго, то потом сборщик мусора (GC) перестанет пытаться освобождать из под него память.
Не перестанет. Просто будет делать это пореже.
КД>И эта мысль меня беспокоит
Забейте. Худший способ вложения усилий — решение проблем, которых у вас нету.
Скорее всего, вы ухудшите наблюдаемые характеристики работы вашей библиотеки, и внесёте трудновоспроизводимые баги.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.