├── .gitignore ├── LICENSE ├── README.md ├── Test ├── Test │ ├── App.config │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Test.csproj │ ├── packages.config │ └── 测试结果.txt ├── WebTest.sln └── WebTest │ ├── App_Start │ ├── LoggerHelper.cs │ └── RouteConfig.cs │ ├── Controllers │ └── TestController.cs │ ├── Global.asax │ ├── Global.asax.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── Test_Insert.sql │ ├── Web.Debug.config │ ├── Web.Release.config │ ├── Web.config │ ├── WebTest.csproj │ └── packages.config ├── ToolGood.AntiDuplication.Redis.StackExchange ├── StackExchangeRedisCache.cs └── ToolGood.AntiDuplication.Redis.StackExchange.csproj ├── ToolGood.AntiDuplication.Test ├── App.config ├── Cache.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── ToolGood.AntiDuplication.Test.csproj ├── ToolGood.AntiDuplication.sln └── ToolGood.AntiDuplication ├── AntiDupCache.cs ├── AntiDupQueue.cs ├── DictCache.cs ├── IConcurrentCache.cs ├── ToolGood.AntiDuplication.csproj └── key.snk /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 ToolGood 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ToolGood.AntiDuplication 2 | === 3 | 4 | 欢迎使用`ToolGood.AntiDuplication`!`ToolGood.AntiDuplication` 是一款轻量级防重复提交组件。 5 | 6 | 7 | ### 快速上手 8 | 9 | ```` csharp 10 | private readonly static AntiDupCache antiDupCache = new AntiDupCache(50, 1); 11 | private readonly static AntiDupQueue antiDupQueue = new AntiDupQueue(50); 12 | private readonly static DictCache dictCache = new DictCache(); 13 | 14 | antiDupCache.GetOrAdd(key, () => { 15 | .... 16 | return val; 17 | }); 18 | 19 | antiDupQueue.GetOrAdd(key, () => { 20 | .... 21 | return val; 22 | }); 23 | 24 | dictCache.GetOrAdd(key, () => { 25 | .... 26 | return val; 27 | }); 28 | 29 | ````` 30 | 31 | 注意:`AntiDupCache`、`AntiDupQueue`、`DictCache`类的`GetOrAdd`方法中的`key`不要设置为`null`,`key`为null时,不缓存值。 32 | 33 | 注意:`DictCache`需要手动调用`Clear`方法清空缓存。 34 | 35 | 36 | 37 | 38 | ### 性能: 39 | ```` 40 | ----------------------- 测试缓存性能 从1到100 重复次数:10000 单位: ms ----------------------- 41 | 并发数量: 1 2 3 4 5 6 7 8 9 10 11 12 42 | AntiDupCache: 275 221 263 254 263 259 243 269 272 255 257 278 43 | AntiDupQueue: 114 119 122 142 143 136 144 148 142 168 173 166 44 | DictCache: 109 117 120 138 139 127 145 150 160 161 160 193 45 | Cache: 91 96 122 113 103 121 125 123 133 128 156 149 46 | 47 | ----------------------- 仿线上环境 从1到1000 单位: ms ----------------------- 48 | 并发数量: 1 2 3 4 5 6 7 8 9 10 11 12 49 | 普通并发: 1891 957 641 476 383 318 278 241 218 199 187 164 50 | AntiDupCache: 1868 959 632 481 380 319 269 241 215 195 193 163 51 | AntiDupQueue: 1897 947 629 477 383 321 273 241 212 195 187 162 52 | DictCache: 1897 954 639 470 382 318 277 239 215 195 187 163 53 | Cache: 1888 1897 1901 1871 1892 1879 1850 1845 1860 1830 1812 1909 54 | 第二次普通并发: 1950 977 646 476 375 327 278 244 218 190 191 161 55 | 56 | ----------------------- 开始 从1到100 重复次数:1 单位: ms ----------------------- 57 | 并发数量: 1 2 3 4 5 6 7 8 9 10 11 12 58 | 普通并发: 185 95 62 46 40 39 28 29 22 21 20 20 59 | AntiDupCache: 188 93 64 45 39 39 28 28 22 20 18 22 60 | AntiDupQueue: 190 93 61 47 37 36 29 29 22 19 18 23 61 | DictCache: 186 95 64 46 37 37 29 30 19 19 18 21 62 | Cache: 191 191 184 186 188 186 181 185 187 186 186 180 63 | 第二次普通并发: 185 93 60 48 34 36 28 30 22 19 17 20 64 | 65 | ----------------------- 开始 从1到100 重复次数:2 单位: ms ----------------------- 66 | 并发数量: 1 2 3 4 5 6 7 8 9 10 11 12 67 | 普通并发: 373 188 126 91 75 62 59 48 45 36 37 59 68 | AntiDupCache: 187 96 63 48 37 34 29 26 23 19 18 24 69 | AntiDupQueue: 191 95 63 47 38 33 29 25 20 17 18 23 70 | DictCache: 190 96 65 47 39 32 28 22 23 19 18 23 71 | Cache: 179 190 181 185 188 186 182 180 178 182 180 180 72 | 第二次普通并发: 374 183 128 94 72 63 60 46 40 36 37 41 73 | 74 | ----------------------- 开始 从1到100 重复次数:3 单位: ms ----------------------- 75 | 并发数量: 1 2 3 4 5 6 7 8 9 10 11 12 76 | 普通并发: 555 283 194 144 113 95 87 77 67 55 56 45 77 | AntiDupCache: 187 94 64 46 39 34 27 28 23 18 18 16 78 | AntiDupQueue: 181 92 66 45 37 38 30 29 24 20 18 14 79 | DictCache: 187 95 67 47 38 32 29 23 22 19 18 15 80 | Cache: 183 188 186 191 188 185 183 186 183 182 182 174 81 | 第二次普通并发: 559 282 192 141 113 94 90 77 66 55 56 45 82 | 83 | ----------------------- 开始 从1到100 重复次数:4 单位: ms ----------------------- 84 | 并发数量: 1 2 3 4 5 6 7 8 9 10 11 12 85 | 普通并发: 744 379 250 193 155 131 111 94 93 83 73 70 86 | AntiDupCache: 188 95 65 42 40 34 29 25 22 19 18 19 87 | AntiDupQueue: 193 93 66 44 41 34 30 26 20 18 20 20 88 | DictCache: 187 96 65 47 37 31 28 26 21 20 19 19 89 | Cache: 183 185 187 190 181 187 181 181 179 182 177 179 90 | 第二次普通并发: 745 375 255 185 155 127 106 92 90 75 74 67 91 | 92 | ----------------------- 开始 从1到100 重复次数:5 单位: ms ----------------------- 93 | 并发数量: 1 2 3 4 5 6 7 8 9 10 11 12 94 | 普通并发: 925 473 317 236 191 162 138 120 110 94 90 91 95 | AntiDupCache: 189 94 65 48 37 35 29 26 21 19 18 19 96 | AntiDupQueue: 193 93 65 47 37 34 28 29 23 21 18 20 97 | DictCache: 194 95 68 49 39 35 31 25 22 22 18 19 98 | Cache: 190 184 193 198 192 188 184 184 197 188 179 178 99 | 第二次普通并发: 927 464 314 232 191 158 138 126 112 93 93 90 100 | 101 | ----------------------- 开始 从1到100 重复次数:6 单位: ms ----------------------- 102 | 并发数量: 1 2 3 4 5 6 7 8 9 10 11 12 103 | 普通并发: 1124 566 380 281 224 190 162 141 138 112 112 95 104 | AntiDupCache: 180 93 61 48 38 32 30 25 25 20 18 20 105 | AntiDupQueue: 184 92 64 48 39 36 31 22 20 17 19 19 106 | DictCache: 193 96 63 48 39 35 31 23 22 19 20 20 107 | Cache: 188 190 185 188 184 189 179 188 176 180 186 181 108 | 第二次普通并发: 1130 563 389 290 229 192 170 146 132 116 114 93 109 | 110 | ----------------------- 开始 从1到100 重复次数:7 单位: ms ----------------------- 111 | 并发数量: 1 2 3 4 5 6 7 8 9 10 11 12 112 | 普通并发: 1312 667 446 330 270 225 190 255 162 134 135 122 113 | AntiDupCache: 198 98 67 47 36 33 32 30 22 17 17 20 114 | AntiDupQueue: 188 92 63 45 39 37 31 29 23 19 19 19 115 | DictCache: 189 93 62 48 39 33 30 25 22 20 18 20 116 | Cache: 192 199 196 194 190 193 199 196 192 184 187 180 117 | 第二次普通并发: 1306 668 430 330 269 230 190 165 154 129 128 116 118 | 119 | ----------------------- 开始 从1到100 重复次数:8 单位: ms ----------------------- 120 | 并发数量: 1 2 3 4 5 6 7 8 9 10 11 12 121 | 普通并发: 1507 759 500 384 300 257 220 189 179 153 148 140 122 | AntiDupCache: 187 95 65 47 42 35 29 27 23 20 18 20 123 | AntiDupQueue: 184 99 63 44 41 37 30 27 23 21 18 19 124 | DictCache: 191 96 66 48 38 35 29 25 22 20 18 18 125 | Cache: 191 187 190 188 188 181 183 181 183 182 180 184 126 | 第二次普通并发: 1499 755 512 382 305 252 218 186 179 151 150 138 127 | 128 | ----------------------- 开始 从1到100 重复次数:9 单位: ms ----------------------- 129 | 并发数量: 1 2 3 4 5 6 7 8 9 10 11 12 130 | 普通并发: 1694 839 564 429 341 286 255 215 193 169 167 143 131 | AntiDupCache: 186 94 67 51 39 37 29 26 24 20 20 18 132 | AntiDupQueue: 198 89 63 51 40 36 30 29 25 19 20 17 133 | DictCache: 183 97 65 45 36 33 31 28 23 22 18 17 134 | Cache: 182 189 191 189 182 176 185 184 179 175 177 176 135 | 第二次普通并发: 1682 843 573 430 345 277 251 218 193 171 168 147 136 | 137 | ----------------------- 开始 从1到100 重复次数:10 单位: ms ----------------------- 138 | 并发数量: 1 2 3 4 5 6 7 8 9 10 11 12 139 | 普通并发: 1863 929 634 474 378 321 274 236 212 189 188 160 140 | AntiDupCache: 190 95 66 47 39 33 30 26 23 21 19 19 141 | AntiDupQueue: 184 94 61 49 39 32 31 23 23 20 17 19 142 | DictCache: 186 96 66 46 39 33 28 24 23 20 18 21 143 | Cache: 184 188 191 188 188 181 182 176 186 189 180 177 144 | 第二次普通并发: 1873 939 639 470 375 323 274 240 214 188 190 164 145 | 146 | ----------------------- 开始 从1到100 重复次数:11 单位: ms ----------------------- 147 | 并发数量: 1 2 3 4 5 6 7 8 9 10 11 12 148 | 普通并发: 2051 1039 698 518 421 349 298 274 233 211 188 185 149 | AntiDupCache: 187 95 63 49 42 37 28 29 23 20 22 21 150 | AntiDupQueue: 187 92 68 48 36 35 31 27 25 19 21 20 151 | DictCache: 199 99 65 48 37 34 31 28 24 19 20 19 152 | Cache: 190 193 197 186 192 193 194 199 194 191 190 191 153 | 第二次普通并发: 2083 1041 695 521 423 351 299 268 239 213 188 182 154 | 155 | ----------------------- 开始 从1到100 重复次数:12 单位: ms ----------------------- 156 | 并发数量: 1 2 3 4 5 6 7 8 9 10 11 12 157 | 普通并发: 2241 1132 757 574 460 379 332 286 258 234 206 186 158 | AntiDupCache: 182 91 63 46 38 34 29 28 24 22 22 20 159 | AntiDupQueue: 183 93 63 47 39 35 29 26 27 21 21 17 160 | DictCache: 185 93 64 46 38 32 29 26 26 20 21 19 161 | Cache: 182 187 190 184 185 187 185 184 176 182 187 175 162 | 第二次普通并发: 2242 1135 757 561 453 376 326 283 252 227 208 192 163 | 164 | ----------------------- 结束 ----------------------- 165 | 166 | 167 | ```` 168 | -------------------------------------------------------------------------------- /Test/Test/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Test/Test/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Net; 5 | using System.Threading.Tasks; 6 | using ToolGood.ReadyGo3; 7 | 8 | namespace Test 9 | { 10 | class Program 11 | { 12 | static void Main(string[] args) 13 | { 14 | var domain = "http://localhost:9988"; 15 | //var domain = "http://localhost:60350"; 16 | 17 | List list = new List(); 18 | for (int i = 0; i < 1000; i++) { 19 | for (int j = 0; j < 4; j++) { 20 | list.Add(i.ToString()); 21 | } 22 | } 23 | var helper = SqlHelperFactory.OpenFormConnStr("Writer"); 24 | 25 | Console.Write("通过select防重复:"); 26 | CallWeb(list, domain + "/test/test1/", helper); 27 | 28 | Console.Write("通过redis锁防重复:"); 29 | CallWeb(list, domain + "/test/test2/", helper); 30 | 31 | Console.Write("通过AntiDupCache防重复:"); 32 | CallWeb(list, domain + "/test/test3/", helper); 33 | 34 | Console.Write("通过AntiDupQueue防重复:"); 35 | CallWeb(list, domain + "/test/test4/", helper); 36 | 37 | Console.ReadKey(); 38 | } 39 | 40 | static void CallWeb(List list, string url, SqlHelper helper) 41 | { 42 | helper.Execute("TRUNCATE TABLE Test_Insert"); 43 | var stopwatch = Stopwatch.StartNew(); 44 | int errorCount = 0; 45 | Parallel.ForEach(list, (str) => { 46 | WebClient webClient = new WebClient(); 47 | var html = webClient.DownloadString(url + str); 48 | webClient.Dispose(); 49 | if (html != str) { 50 | errorCount++; 51 | } 52 | }); 53 | stopwatch.Stop(); 54 | Console.Write(stopwatch.ElapsedMilliseconds + "ms\r\n"); 55 | Console.WriteLine("插入个数:" + helper.First("select count(*) from Test_Insert").ToString()); 56 | if (errorCount > 0) { 57 | Console.WriteLine("错误次数:" + errorCount.ToString()); 58 | } 59 | } 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Test/Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("Test")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Test")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("c3ac82ff-e031-4494-819e-3354ff9a8d06")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 33 | // 方法是按如下所示使用“*”: : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Test/Test/Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {C3AC82FF-E031-4494-819E-3354FF9A8D06} 8 | Exe 9 | Test 10 | Test 11 | v4.5.1 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | ..\packages\ToolGood.ReadyGo3.3.1.1.6\lib\net45\ToolGood.ReadyGo3.dll 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Test/Test/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Test/Test/测试结果.txt: -------------------------------------------------------------------------------- 1 | 通过select防重复:6032ms 2 | 插入个数:1015 3 | 通过redis锁防重复:6668ms 4 | 插入个数:1000 5 | 错误次数:54 6 | 通过AntiDupCache防重复:1245ms 7 | 插入个数:1000 8 | 通过AntiDupQueue防重复:1140ms 9 | 插入个数:1000 10 | 11 | 12 | 通过select防重复:2494ms 13 | 插入个数:1007 14 | 通过redis锁防重复:7018ms 15 | 插入个数:1000 16 | 错误次数:55 17 | 通过AntiDupCache防重复:1161ms 18 | 插入个数:1000 19 | 通过AntiDupQueue防重复:1189ms 20 | 插入个数:1000 21 | 22 | 23 | 通过select防重复:2384ms 24 | 插入个数:1004 25 | 通过redis锁防重复:9476ms 26 | 插入个数:1000 27 | 错误次数:31 28 | 通过AntiDupCache防重复:888ms 29 | 插入个数:1000 30 | 通过AntiDupQueue防重复:795ms 31 | 插入个数:1000 32 | 33 | 34 | 通过select防重复:2934ms 35 | 插入个数:1001 36 | 通过redis锁防重复:7736ms 37 | 插入个数:1000 38 | 错误次数:20 39 | 通过AntiDupCache防重复:1394ms 40 | 插入个数:1000 41 | 通过AntiDupQueue防重复:1311ms 42 | 插入个数:1000 -------------------------------------------------------------------------------- /Test/WebTest.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.168 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebTest", "WebTest\WebTest.csproj", "{6C81EFC1-33A1-41D7-8212-C28D5EF27245}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{C3AC82FF-E031-4494-819E-3354FF9A8D06}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {6C81EFC1-33A1-41D7-8212-C28D5EF27245}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {6C81EFC1-33A1-41D7-8212-C28D5EF27245}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {6C81EFC1-33A1-41D7-8212-C28D5EF27245}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {6C81EFC1-33A1-41D7-8212-C28D5EF27245}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {C3AC82FF-E031-4494-819E-3354FF9A8D06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {C3AC82FF-E031-4494-819E-3354FF9A8D06}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {C3AC82FF-E031-4494-819E-3354FF9A8D06}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {C3AC82FF-E031-4494-819E-3354FF9A8D06}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {4423355D-BC41-4134-8FC8-55F57962BA24} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /Test/WebTest/App_Start/LoggerHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Text.RegularExpressions; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using System.Web; 11 | 12 | namespace SecondPartyManage.BaseCodes 13 | { 14 | public static class LoggerHelper 15 | { 16 | private readonly static FileLoggerQueue errorLogger = new FileLoggerQueue(10000, "/logs/error/error-{yyyy}{MM}{dd}.log"); 17 | private readonly static FileLoggerQueue infoLogger = new FileLoggerQueue(10000, "/logs/info/info-{yyyy}{MM}{dd}.log"); 18 | private readonly static FileLoggerQueue debugLogger = new FileLoggerQueue(10000, "/logs/debug/debug-{yyyy}{MM}{dd}.log"); 19 | private readonly static FileLoggerQueue fatalLogger = new FileLoggerQueue(10000, "/logs/fatal/fatal-{yyyy}{MM}{dd}.log"); 20 | private readonly static FileLoggerQueue warnLogger = new FileLoggerQueue(10000, "/logs/warn/warn-{yyyy}{MM}{dd}.log"); 21 | private readonly static FileLoggerQueue traceLogger = new FileLoggerQueue(10000, "/logs/trace/trace-{yyyy}{MM}{dd}.log"); 22 | 23 | 24 | public static void Error(string content) 25 | { 26 | var ip = GetWebClientIp(); 27 | var request = HttpContext.Current.Request; 28 | var msg = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}|{ip}|{request.HttpMethod}|{request.Url}\r\n{content}\r\n\r\n"; 29 | errorLogger.EnqueueMessage(msg); 30 | } 31 | public static void Info(string content) 32 | { 33 | var ip = GetWebClientIp(); 34 | var request = HttpContext.Current.Request; 35 | var msg = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}|{ip}|{request.HttpMethod}|{request.Url}\r\n{content}\r\n\r\n"; 36 | infoLogger.EnqueueMessage(msg); 37 | } 38 | public static void Debug(string content) 39 | { 40 | var ip = GetWebClientIp(); 41 | var request = HttpContext.Current.Request; 42 | var msg = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}|{ip}|{request.HttpMethod}|{request.Url}\r\n{content}\r\n\r\n"; 43 | debugLogger.EnqueueMessage(msg); 44 | } 45 | public static void Fatal(string content) 46 | { 47 | var ip = GetWebClientIp(); 48 | var request = HttpContext.Current.Request; 49 | var msg = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}|{ip}|{request.HttpMethod}|{request.Url}\r\n{content}\r\n\r\n"; 50 | fatalLogger.EnqueueMessage(msg); 51 | } 52 | public static void Warn(string content) 53 | { 54 | var ip = GetWebClientIp(); 55 | var request = HttpContext.Current.Request; 56 | var msg = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}|{ip}|{request.HttpMethod}|{request.Url}\r\n{content}\r\n\r\n"; 57 | warnLogger.EnqueueMessage(msg); 58 | } 59 | public static void Trace(string content) 60 | { 61 | var ip = GetWebClientIp(); 62 | var request = HttpContext.Current.Request; 63 | var msg = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}|{ip}|{request.HttpMethod}|{request.Url}\r\n{content}\r\n\r\n"; 64 | traceLogger.EnqueueMessage(msg); 65 | } 66 | 67 | class FileLoggerQueue 68 | { 69 | private ConcurrentQueue _writeQueue = new ConcurrentQueue(); 70 | private int _isInProcessMessage = 0; 71 | private readonly string _filePath; 72 | private readonly int _maxWriteCount; 73 | 74 | 75 | public FileLoggerQueue(int maxWriteCount, string filePath) 76 | { 77 | _maxWriteCount = maxWriteCount; 78 | _filePath = filePath; 79 | } 80 | 81 | public void EnqueueMessage(string info) 82 | { 83 | _writeQueue.Enqueue(info); 84 | ProcessMessage(); 85 | if (_writeQueue.Count >= _maxWriteCount) Thread.Sleep(1); 86 | } 87 | 88 | private void ProcessMessage() 89 | { 90 | bool flag = Interlocked.CompareExchange(ref _isInProcessMessage, 1, 0) == 0; 91 | if (flag == false) return; 92 | 93 | Task.Factory.StartNew(() => { 94 | try { 95 | if (_writeQueue.IsEmpty) return; 96 | 97 | var filePath = GetFullPath(_filePath, DateTime.Now); 98 | Directory.CreateDirectory(Path.GetDirectoryName(filePath)); 99 | var fs = File.OpenWrite(filePath); 100 | 101 | fs.Seek(0, SeekOrigin.End); 102 | while (_writeQueue.TryDequeue(out string info)) { 103 | var bytes = Encoding.UTF8.GetBytes(info); 104 | fs.Write(bytes, 0, bytes.Length); 105 | } 106 | fs.Close(); 107 | } finally { 108 | Interlocked.Exchange(ref _isInProcessMessage, 0); 109 | if (!_writeQueue.IsEmpty) ProcessMessage(); 110 | } 111 | }); 112 | } 113 | 114 | private string GetFullPath(string filePath, DateTime dateTime) 115 | { 116 | if (filePath.Contains("{")) { 117 | var invalidPattern = new Regex(@"[\*\?\042\<\>\|]", RegexOptions.Compiled); 118 | filePath = invalidPattern.Replace(filePath, ""); 119 | StringBuilder file = new StringBuilder(filePath); 120 | 121 | file.Replace("{time}", (dateTime.Ticks / 1000).ToString()); 122 | file.Replace("{yyyy}", dateTime.Year.ToString()); 123 | file.Replace("{yy}", (dateTime.Year % 100).ToString("D2")); 124 | file.Replace("{MM}", dateTime.Month.ToString("D2")); 125 | file.Replace("{dd}", dateTime.Day.ToString("D2")); 126 | file.Replace("{HH}", dateTime.Hour.ToString("D2")); 127 | file.Replace("{hh}", dateTime.Hour.ToString("D2")); 128 | file.Replace("{mm}", dateTime.Minute.ToString("D2")); 129 | file.Replace("{ss}", dateTime.Second.ToString("D2")); 130 | filePath = file.ToString(); 131 | } 132 | return System.Web.Hosting.HostingEnvironment.MapPath(filePath); 133 | //return Path.GetFullPath(filePath); 134 | } 135 | 136 | 137 | } 138 | 139 | 140 | /// 141 | /// 获取web客户端ip 142 | /// 143 | /// 144 | private static string GetWebClientIp() 145 | { 146 | 147 | string userIP = "未获取用户IP"; 148 | 149 | try { 150 | if (System.Web.HttpContext.Current == null 151 | || System.Web.HttpContext.Current.Request == null 152 | || System.Web.HttpContext.Current.Request.ServerVariables == null) { 153 | return ""; 154 | } 155 | 156 | string CustomerIP = ""; 157 | 158 | //CDN加速后取到的IP simone 090805 159 | CustomerIP = System.Web.HttpContext.Current.Request.Headers["Cdn-Src-Ip"]; 160 | if (!string.IsNullOrEmpty(CustomerIP)) { 161 | return CustomerIP; 162 | } 163 | 164 | CustomerIP = System.Web.HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"]; 165 | 166 | if (!String.IsNullOrEmpty(CustomerIP)) { 167 | return CustomerIP; 168 | } 169 | 170 | if (System.Web.HttpContext.Current.Request.ServerVariables["HTTP_VIA"] != null) { 171 | CustomerIP = System.Web.HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"]; 172 | 173 | if (CustomerIP == null) { 174 | CustomerIP = System.Web.HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"]; 175 | } 176 | } else { 177 | CustomerIP = System.Web.HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"]; 178 | } 179 | 180 | if (string.Compare(CustomerIP, "unknown", true) == 0 || String.IsNullOrEmpty(CustomerIP)) { 181 | return System.Web.HttpContext.Current.Request.UserHostAddress; 182 | } 183 | return CustomerIP; 184 | } catch { } 185 | 186 | return userIP; 187 | 188 | } 189 | } 190 | 191 | } -------------------------------------------------------------------------------- /Test/WebTest/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using System.Web.Routing; 7 | 8 | namespace WebTest 9 | { 10 | public class RouteConfig 11 | { 12 | public static void RegisterRoutes(RouteCollection routes) 13 | { 14 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 15 | 16 | routes.MapRoute( 17 | name: "Default", 18 | url: "{controller}/{action}/{id}", 19 | defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } 20 | ); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Test/WebTest/Controllers/TestController.cs: -------------------------------------------------------------------------------- 1 | using SecondPartyManage.BaseCodes; 2 | using StackExchange.Redis; 3 | using System; 4 | using System.Configuration; 5 | using System.Web.Mvc; 6 | using ToolGood.AntiDuplication; 7 | using ToolGood.ReadyGo3; 8 | 9 | namespace WebTest.Controllers 10 | { 11 | public class TestController : Controller 12 | { 13 | public ActionResult Test1(string id) 14 | { 15 | using (var helper = SqlHelperFactory.OpenFormConnStr("Writer")) { 16 | var count = helper.First("select Count(*) from Test_Insert where FNum=@0", id); 17 | if (count == 0) { 18 | helper.Execute("INSERT INTO Test_Insert (FNum) VALUES (@0);", id); 19 | } 20 | } 21 | return Content(id); 22 | } 23 | 24 | public ActionResult Test2(string id) 25 | { 26 | var rediesConnStr = ConfigurationManager.ConnectionStrings["redis"].ConnectionString; 27 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(rediesConnStr); 28 | try { 29 | IDatabase db = redis.GetDatabase(2); 30 | if (db.LockTake("fnum_" + id, "1", TimeSpan.FromSeconds(1))) { 31 | using (var helper = SqlHelperFactory.OpenFormConnStr("Writer")) { 32 | var count = helper.First("select Count(*) from Test_Insert where FNum=@0", id); 33 | if (count == 0) { 34 | helper.Execute("INSERT INTO Test_Insert (FNum) VALUES (@0);", id); 35 | } 36 | } 37 | db.LockRelease("fnum_" + id, "1"); 38 | return Content(id); 39 | } 40 | } catch (Exception ex) { 41 | LoggerHelper.Error(ex.Message); 42 | }finally { 43 | redis.Close(); 44 | } 45 | return Content("Error"); 46 | } 47 | 48 | private static AntiDupCache cache = new AntiDupCache(20,1); 49 | public ActionResult Test3(string id) 50 | { 51 | var str = cache.Execute(id, () => { 52 | using (var helper = SqlHelperFactory.OpenFormConnStr("Writer")) { 53 | var count = helper.First("select Count(*) from Test_Insert where FNum=@0", id); 54 | if (count == 0) { 55 | helper.Execute("INSERT INTO Test_Insert (FNum) VALUES (@0);", id); 56 | } 57 | } 58 | return id; 59 | }); 60 | return Content(str); 61 | } 62 | 63 | 64 | private static AntiDupQueue queue = new AntiDupQueue(20); 65 | public ActionResult Test4(string id) 66 | { 67 | var str = queue.Execute(id, () => { 68 | using (var helper = SqlHelperFactory.OpenFormConnStr("Writer")) { 69 | var count = helper.First("select Count(*) from Test_Insert where FNum=@0", id); 70 | if (count == 0) { 71 | helper.Execute("INSERT INTO Test_Insert (FNum) VALUES (@0);", id); 72 | } 73 | } 74 | return id; 75 | }); 76 | return Content(str); 77 | } 78 | 79 | 80 | } 81 | 82 | } -------------------------------------------------------------------------------- /Test/WebTest/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="WebTest.MvcApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /Test/WebTest/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using System.Web.Routing; 7 | 8 | namespace WebTest 9 | { 10 | public class MvcApplication : System.Web.HttpApplication 11 | { 12 | protected void Application_Start() 13 | { 14 | AreaRegistration.RegisterAllAreas(); 15 | RouteConfig.RegisterRoutes(RouteTable.Routes); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Test/WebTest/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的常规信息通过下列特性集 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("WebTest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("WebTest")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | // 对 COM 组件不可见。如果需要 19 | // 从 COM 访问此程序集中的某个类型,请针对该类型将 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于 typelib 的 ID 23 | [assembly: Guid("6c81efc1-33a1-41d7-8212-c28d5ef27245")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 内部版本号 30 | // 修订版本 31 | // 32 | // 可以指定所有值,也可以使用“修订号”和“内部版本号”的默认值, 33 | // 方法是按如下所示使用 "*": 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /Test/WebTest/Test_Insert.sql: -------------------------------------------------------------------------------- 1 | 2 | 3 | -- ---------------------------- 4 | -- Table structure for Test_Insert 5 | -- ---------------------------- 6 | 7 | CREATE TABLE [dbo].[Test_Insert] ( 8 | [FID] int NOT NULL IDENTITY(1,1) , 9 | [FNum] varchar(50) NULL 10 | ) 11 | 12 | GO 13 | 14 | 15 | -- ---------------------------- 16 | -- Primary Key structure for table Test_Insert 17 | -- ---------------------------- 18 | ALTER TABLE [dbo].[Test_Insert] ADD PRIMARY KEY ([FID]) 19 | GO 20 | -------------------------------------------------------------------------------- /Test/WebTest/Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 30 | 31 | -------------------------------------------------------------------------------- /Test/WebTest/Web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 31 | 32 | -------------------------------------------------------------------------------- /Test/WebTest/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Test/WebTest/WebTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 7 | 8 | 2.0 9 | {6C81EFC1-33A1-41D7-8212-C28D5EF27245} 10 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} 11 | Library 12 | Properties 13 | WebTest 14 | WebTest 15 | v4.5.1 16 | true 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | true 28 | full 29 | false 30 | bin\ 31 | DEBUG;TRACE 32 | prompt 33 | 4 34 | 35 | 36 | true 37 | pdbonly 38 | true 39 | bin\ 40 | TRACE 41 | prompt 42 | 4 43 | 44 | 45 | 46 | 47 | ..\packages\StackExchange.Redis.1.2.4\lib\net45\StackExchange.Redis.dll 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | ..\packages\ToolGood.AntiDuplication.1.0.0.4\lib\net45\ToolGood.AntiDuplication.dll 69 | 70 | 71 | ..\packages\ToolGood.ReadyGo3.3.1.1.6\lib\net45\ToolGood.ReadyGo3.dll 72 | 73 | 74 | 75 | 76 | ..\packages\Microsoft.AspNet.Razor.3.2.4\lib\net45\System.Web.Razor.dll 77 | 78 | 79 | ..\packages\Microsoft.AspNet.Webpages.3.2.4\lib\net45\System.Web.Webpages.dll 80 | 81 | 82 | ..\packages\Microsoft.AspNet.Webpages.3.2.4\lib\net45\System.Web.Webpages.Deployment.dll 83 | 84 | 85 | ..\packages\Microsoft.AspNet.Webpages.3.2.4\lib\net45\System.Web.Webpages.Razor.dll 86 | 87 | 88 | ..\packages\Microsoft.AspNet.Webpages.3.2.4\lib\net45\System.Web.Helpers.dll 89 | 90 | 91 | ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll 92 | 93 | 94 | ..\packages\Microsoft.AspNet.Mvc.5.2.4\lib\net45\System.Web.Mvc.dll 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | Global.asax 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | Web.config 116 | 117 | 118 | Web.config 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 10.0 127 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | True 137 | True 138 | 60350 139 | / 140 | http://localhost:60350/ 141 | False 142 | False 143 | 144 | 145 | False 146 | 147 | 148 | 149 | 150 | 157 | -------------------------------------------------------------------------------- /Test/WebTest/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /ToolGood.AntiDuplication.Redis.StackExchange/StackExchangeRedisCache.cs: -------------------------------------------------------------------------------- 1 | //using System; 2 | //using System.Collections.Generic; 3 | //using System.Linq; 4 | //using System.Text; 5 | //using System.Threading.Tasks; 6 | //using StackExchange.Redis; 7 | //using Newtonsoft.Json; 8 | 9 | //namespace ToolGood.AntiDuplication.Redis.StackExchange 10 | //{ 11 | // public class StackExchangeRedisCache : IConcurrentCache 12 | // { 13 | // private readonly string _rediesConnStr; 14 | // private readonly int _databaseId; 15 | // private readonly string _prefix; 16 | // private readonly string _lockPrefix; 17 | // private readonly int _timeout; 18 | 19 | // public StackExchangeRedisCache(string rediesConnStr, int databaseId, string prefix, string lockPrefix, int timeout) 20 | // { 21 | // _rediesConnStr = rediesConnStr; 22 | // _databaseId = databaseId; 23 | // _prefix = prefix; 24 | // _lockPrefix = lockPrefix; 25 | // _timeout = timeout; 26 | // } 27 | 28 | // #region 属性 29 | // /// 30 | // /// 获取缓存个数 31 | // /// 32 | // public int Count { 33 | // get { 34 | // return 0; 35 | // } 36 | // } 37 | 38 | // /// 39 | // /// 是否为空 40 | // /// 41 | // public bool IsEmpty { 42 | // get { 43 | // return 0; 44 | // } 45 | // } 46 | // #endregion 47 | 48 | // #region TValue 49 | 50 | // /// 51 | // /// 获取或添加缓存 52 | // /// 53 | // /// 关键字 54 | // /// 每次超时秒数 55 | // /// 值 56 | // /// 57 | // public TValue GetOrAdd(TKey key, TValue val, int secord = 0) 58 | // { 59 | // // 过期时间为0 则不缓存 60 | // if (object.Equals(null, key)) { return val; } 61 | 62 | // TValue value = default(TValue); 63 | // long lastTicks = 0; 64 | // if (tryGet(key, ref value, ref lastTicks, secord)) { return value; } 65 | 66 | // AntiDupLockSlim slim; 67 | // _slimLock.EnterUpgradeableReadLock(); 68 | // try { 69 | // if (checkGet(key, lastTicks, ref value, secord)) { return value; } 70 | // slim = addLock(key); 71 | // } finally { _slimLock.ExitUpgradeableReadLock(); } 72 | 73 | // slim.EnterWriteLock(); 74 | // try { 75 | // if (checkGet(key, lastTicks, ref value, secord)) { return value; } 76 | // trySet(key, val, secord); 77 | // return val; 78 | // } finally { 79 | // slim.ExitWriteLock(); 80 | // removeLock(key, slim); 81 | // } 82 | // } 83 | 84 | // /// 85 | // /// 设置缓存 86 | // /// 87 | // /// 关键字 88 | // /// 每次超时秒数 89 | // /// 值 90 | // public void SetValue(TKey key, TValue value, int secord = 0) 91 | // { 92 | // if (object.Equals(null, key)) return; 93 | // trySet(key, value, secord); 94 | // } 95 | 96 | // /// 97 | // /// 添加或更新缓存 98 | // /// 99 | // /// 关键字 100 | // /// 添加值 101 | // /// 更新值 102 | // /// 每次超时秒数 103 | // /// 104 | // public TValue AddOrUpdate(TKey key, TValue addValue, TValue updateValue, int secord = 0) 105 | // { 106 | // if (object.Equals(null, key)) { return addValue; } 107 | 108 | // TValue value = default(TValue); 109 | // long lastTicks = 0; 110 | // if (tryGet(key, ref value, ref lastTicks, secord)) { 111 | // trySet(key, updateValue); 112 | // return value; 113 | // } 114 | 115 | // AntiDupLockSlim slim; 116 | // _slimLock.EnterUpgradeableReadLock(); 117 | // try { 118 | // if (checkGet(key, lastTicks, ref value, secord)) { return value; } 119 | // slim = addLock(key); 120 | // } finally { _slimLock.ExitUpgradeableReadLock(); } 121 | 122 | // slim.EnterWriteLock(); 123 | // try { 124 | // if (checkGet(key, lastTicks, ref value, secord)) { return value; } 125 | // trySet(key, addValue, secord); 126 | // return addValue; 127 | // } finally { 128 | // slim.ExitWriteLock(); 129 | // removeLock(key, slim); 130 | // } 131 | // } 132 | 133 | // /// 134 | // /// 尝试获取缓存 135 | // /// 136 | // /// 关键字 137 | // /// 每次超时秒数 138 | // /// 值 139 | // /// 140 | // public bool TryGetValue(TKey key, out TValue value, int secord = 0) 141 | // { 142 | // value = default(TValue); 143 | // if (object.Equals(null, key)) { return false; } 144 | 145 | // long lastTicks = 0; 146 | // if (tryGet(key, ref value, ref lastTicks, secord)) { return true; } 147 | // return false; 148 | // } 149 | 150 | // /// 151 | // /// 尝试添加缓存 152 | // /// 153 | // /// 关键字 154 | // /// 值 155 | // /// 每次超时秒数 156 | // /// 157 | // public bool TryAdd(TKey key, TValue value, int secord = 0) 158 | // { 159 | // if (object.Equals(null, key)) { return false; } 160 | 161 | // long lastTicks = 0; 162 | // if (tryGet(key, ref value, ref lastTicks, secord) == false) { 163 | // trySet(key, value, secord); 164 | // return true; 165 | // } 166 | // return false; 167 | // } 168 | // /// 169 | // /// 尝试更新缓存 170 | // /// 171 | // /// 关键字 172 | // /// 新值 173 | // /// 每次超时秒数 174 | // /// 175 | // public bool TryUpdate(TKey key, TValue newValue, int secord = 0) 176 | // { 177 | // if (object.Equals(null, key)) { return false; } 178 | 179 | // TValue value = default(TValue); 180 | // long lastTicks = 0; 181 | // if (tryGet(key, ref value, ref lastTicks, secord)) { 182 | // trySet(key, newValue); 183 | // return true; 184 | // } 185 | // return false; 186 | // } 187 | // /// 188 | // /// 尝试更新缓存 189 | // /// 190 | // /// 关键字 191 | // /// 新值 192 | // /// 原值 193 | // /// 每次超时秒数 194 | // /// 195 | // public bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue, int secord = 0) 196 | // { 197 | // if (object.Equals(null, key)) { return false; } 198 | 199 | // TValue value = default(TValue); 200 | // long lastTicks = 0; 201 | // if (tryGet(key, ref value, ref lastTicks, secord)) { 202 | // if (object.Equals(value, comparisonValue)) { 203 | // trySet(key, newValue); 204 | // } 205 | // return true; 206 | // } 207 | // return false; 208 | // } 209 | // /// 210 | // /// 尝试删除缓存 211 | // /// 212 | // /// 关键字 213 | // /// 值 214 | // /// 每次超时秒数 215 | // /// 216 | // public bool TryRemove(TKey key, out TValue value, int secord = 0) 217 | // { 218 | // value = default(TValue); 219 | // if (object.Equals(null, key)) { return false; } 220 | // return tryRemove(key, ref value, secord); 221 | // } 222 | // /// 223 | // /// 删除缓存 224 | // /// 225 | // /// 关键字 226 | // /// 每次超时秒数 227 | // public void Remove(TKey key, int secord = 0) 228 | // { 229 | // if (object.Equals(null, key)) { return; } 230 | // tryRemove(key, secord); 231 | // } 232 | // /// 233 | // /// 是否包含缓存 234 | // /// 235 | // /// 236 | // /// 237 | // /// 238 | // public bool ContainsKey(TKey key, int secord = 0) 239 | // { 240 | // if (object.Equals(null, key)) { return false; } 241 | 242 | // return containsKey(key, secord); 243 | // } 244 | 245 | // #endregion 246 | 247 | // #region Func factory 248 | // /// 249 | // /// 获取或添加缓存 250 | // /// 251 | // /// 关键字 252 | // /// 每次超时秒数 253 | // /// 执行方法 254 | // /// 255 | // public TValue GetOrAdd(TKey key, Func factory, int secord = 0) 256 | // { 257 | // // 过期时间为0 则不缓存 258 | // if (object.Equals(null, key)) { return factory(); } 259 | 260 | // TValue value = default(TValue); 261 | // long lastTicks = 0; 262 | // if (tryGet(key, ref value, ref lastTicks, secord)) { return value; } 263 | 264 | // AntiDupLockSlim slim; 265 | // _slimLock.EnterUpgradeableReadLock(); 266 | // try { 267 | // if (checkGet(key, lastTicks, ref value, secord)) { return value; } 268 | // slim = addLock(key); 269 | // } finally { _slimLock.ExitUpgradeableReadLock(); } 270 | 271 | // slim.EnterWriteLock(); 272 | // try { 273 | // if (checkGet(key, lastTicks, ref value, secord)) { return value; } 274 | // var val = factory(); 275 | // trySet(key, val, secord); 276 | // return val; 277 | // } finally { 278 | // slim.ExitWriteLock(); 279 | // removeLock(key, slim); 280 | // } 281 | // } 282 | // /// 283 | // /// 设置缓存 284 | // /// 285 | // /// 关键字 286 | // /// 执行方法 287 | // /// 每次超时秒数 288 | // /// 289 | // public TValue SetValue(TKey key, Func factory, int secord = 0) 290 | // { 291 | // if (object.Equals(null, key)) return factory(); 292 | // var value = factory(); 293 | // trySet(key, value, secord); 294 | // return value; 295 | // } 296 | // /// 297 | // /// 添加或更新缓存 298 | // /// 299 | // /// 关键字 300 | // /// 添加的值 301 | // /// 更新的值 302 | // /// 每次超时秒数 303 | // /// 304 | // public TValue AddOrUpdate(TKey key, Func addValueFactory, Func updateValueFactory, int secord) 305 | // { 306 | // if (object.Equals(null, key)) { return addValueFactory(); } 307 | 308 | // TValue value = default(TValue); 309 | // long lastTicks = 0; 310 | // if (tryGet(key, ref value, ref lastTicks, secord)) { 311 | // value = updateValueFactory(); 312 | // trySet(key, value); 313 | // return value; 314 | // } 315 | 316 | // AntiDupLockSlim slim; 317 | // _slimLock.EnterUpgradeableReadLock(); 318 | // try { 319 | // if (checkGet(key, lastTicks, ref value, secord)) { return value; } 320 | // slim = addLock(key); 321 | // } finally { _slimLock.ExitUpgradeableReadLock(); } 322 | 323 | // slim.EnterWriteLock(); 324 | // try { 325 | // if (checkGet(key, lastTicks, ref value, secord)) { return value; } 326 | // var val = addValueFactory(); 327 | // trySet(key, val, secord); 328 | // return val; 329 | // } finally { 330 | // slim.ExitWriteLock(); 331 | // removeLock(key, slim); 332 | // } 333 | // } 334 | // /// 335 | // /// 尝试添加缓存 336 | // /// 337 | // /// 关键字 338 | // /// 339 | // /// 每次超时秒数 340 | // /// 341 | // public bool TryAdd(TKey key, Func factory, int secord = 0) 342 | // { 343 | // if (object.Equals(null, key)) { return false; } 344 | // var value = factory(); 345 | // trySet(key, value, secord); 346 | // return true; 347 | // } 348 | // /// 349 | // /// 尝试更新缓存 350 | // /// 351 | // /// 关键字 352 | // /// 更新的值 353 | // /// 每次超时秒数 354 | // /// 355 | // public bool TryUpdate(TKey key, Func updateValueFactory, int secord = 0) 356 | // { 357 | // if (object.Equals(null, key)) { return false; } 358 | 359 | // TValue value = default(TValue); 360 | // long lastTicks = 0; 361 | // if (tryGet(key, ref value, ref lastTicks, secord)) { 362 | // trySet(key, updateValueFactory(), secord); 363 | // return true; 364 | // } 365 | // return false; 366 | // } 367 | // /// 368 | // /// 尝试更新缓存 369 | // /// 370 | // /// 关键字 371 | // /// 更新的值 372 | // /// 原值 373 | // /// 每次超时秒数 374 | // /// 375 | // public bool TryUpdate(TKey key, Func updateValueFactory, TValue comparisonValue, int secord = 0) 376 | // { 377 | // if (object.Equals(null, key)) { return false; } 378 | 379 | // TValue value = default(TValue); 380 | // long lastTicks = 0; 381 | // if (tryGet(key, ref value, ref lastTicks, secord)) { 382 | // if (object.Equals(value, comparisonValue)) { 383 | // trySet(key, updateValueFactory(), secord); 384 | // } 385 | // return true; 386 | // } 387 | // return false; 388 | // } 389 | 390 | // #endregion 391 | 392 | // #region async Func factory 393 | //#if !NET40 394 | // /// 395 | // /// 获取或添加缓存 396 | // /// 397 | // /// 关键字 398 | // /// 每次超时秒数 399 | // /// 执行方法 400 | // /// 401 | // public async Task GetOrAdd(TKey key, Func> factory, int secord = 0) 402 | // { 403 | // // 过期时间为0 则不缓存 404 | // if (object.Equals(null, key)) { return await factory(); } 405 | 406 | // TValue value = default(TValue); 407 | // long lastTicks = 0; 408 | // if (tryGet(key, ref value, ref lastTicks, secord)) { return value; } 409 | 410 | // AntiDupLockSlim slim; 411 | // _slimLock.EnterUpgradeableReadLock(); 412 | // try { 413 | // if (checkGet(key, lastTicks, ref value, secord)) { return value; } 414 | // slim = addLock(key); 415 | // } finally { _slimLock.ExitUpgradeableReadLock(); } 416 | 417 | // slim.EnterWriteLock(); 418 | // try { 419 | // if (checkGet(key, lastTicks, ref value, secord)) { return value; } 420 | // var val = await factory(); 421 | // trySet(key, val, secord); 422 | // return val; 423 | // } finally { 424 | // slim.ExitWriteLock(); 425 | // removeLock(key, slim); 426 | // } 427 | // } 428 | // /// 429 | // /// 设置缓存 430 | // /// 431 | // /// 关键字 432 | // /// 执行方法 433 | // /// 每次超时秒数 434 | // /// 435 | // public async Task SetValue(TKey key, Func> factory, int secord = 0) 436 | // { 437 | // if (object.Equals(null, key)) return await factory(); 438 | // var value = await factory(); 439 | // trySet(key, value, secord); 440 | // return value; 441 | // } 442 | // /// 443 | // /// 添加或更新缓存 444 | // /// 445 | // /// 关键字 446 | // /// 添加的值 447 | // /// 更新的值 448 | // /// 每次超时秒数 449 | // /// 450 | // public async Task AddOrUpdate(TKey key, Func> addValueFactory, Func> updateValueFactory, int secord) 451 | // { 452 | // if (object.Equals(null, key)) { return await addValueFactory(); } 453 | 454 | // TValue value = default(TValue); 455 | // long lastTicks = 0; 456 | // if (tryGet(key, ref value, ref lastTicks, secord)) { 457 | // value = await updateValueFactory(); 458 | // trySet(key, value); 459 | // return value; 460 | // } 461 | 462 | // AntiDupLockSlim slim; 463 | // _slimLock.EnterUpgradeableReadLock(); 464 | // try { 465 | // if (checkGet(key, lastTicks, ref value, secord)) { return value; } 466 | // slim = addLock(key); 467 | // } finally { _slimLock.ExitUpgradeableReadLock(); } 468 | 469 | // slim.EnterWriteLock(); 470 | // try { 471 | // if (checkGet(key, lastTicks, ref value, secord)) { return value; } 472 | // var val = await addValueFactory(); 473 | // trySet(key, val, secord); 474 | // return val; 475 | // } finally { 476 | // slim.ExitWriteLock(); 477 | // removeLock(key, slim); 478 | // } 479 | // } 480 | // /// 481 | // /// 尝试添加缓存 482 | // /// 483 | // /// 关键字 484 | // /// 485 | // /// 每次超时秒数 486 | // /// 487 | // public async Task TryAdd(TKey key, Func> factory, int secord = 0) 488 | // { 489 | // if (object.Equals(null, key)) { return false; } 490 | // var value = await factory(); 491 | // trySet(key, value, secord); 492 | // return true; 493 | // } 494 | // /// 495 | // /// 尝试更新缓存 496 | // /// 497 | // /// 关键字 498 | // /// 更新的值 499 | // /// 每次超时秒数 500 | // /// 501 | // public async Task TryUpdate(TKey key, Func> updateValueFactory, int secord = 0) 502 | // { 503 | // if (object.Equals(null, key)) { return false; } 504 | 505 | // TValue value = default(TValue); 506 | // long lastTicks = 0; 507 | // if (tryGet(key, ref value, ref lastTicks, secord)) { 508 | // trySet(key, await updateValueFactory(), secord); 509 | // return true; 510 | // } 511 | // return false; 512 | // } 513 | // /// 514 | // /// 尝试更新缓存 515 | // /// 516 | // /// 关键字 517 | // /// 更新的值 518 | // /// 原值 519 | // /// 每次超时秒数 520 | // /// 521 | // public async Task TryUpdate(TKey key, Func> updateValueFactory, TValue comparisonValue, int secord = 0) 522 | // { 523 | // if (object.Equals(null, key)) { return false; } 524 | 525 | // TValue value = default(TValue); 526 | // long lastTicks = 0; 527 | // if (tryGet(key, ref value, ref lastTicks, secord)) { 528 | // if (object.Equals(value, comparisonValue)) { 529 | // trySet(key, await updateValueFactory(), secord); 530 | // } 531 | // return true; 532 | // } 533 | // return false; 534 | // } 535 | // /// 536 | // /// 尝试更新缓存 537 | // /// 538 | // /// 关键字 539 | // /// 更新的值 540 | // /// 原值 541 | // /// 每次超时秒数 542 | // /// 543 | // public async Task TryUpdate(TKey key, Func> updateValueFactory, Func> comparisonValueFactory, int secord = 0) 544 | // { 545 | // if (object.Equals(null, key)) { return false; } 546 | 547 | // TValue value = default(TValue); 548 | // long lastTicks = 0; 549 | // if (tryGet(key, ref value, ref lastTicks, secord)) { 550 | // if (object.Equals(value, await comparisonValueFactory())) { 551 | // trySet(key, await updateValueFactory(), secord); 552 | // } 553 | // return true; 554 | // } 555 | // return false; 556 | // } 557 | 558 | //#endif 559 | // #endregion 560 | 561 | // #region 清空 562 | // /// 563 | // /// 清空 564 | // /// 565 | // public void Clear() 566 | // { 567 | // _lock.EnterWriteLock(); 568 | // try { 569 | // _map.Clear(); 570 | // _slimLock.EnterWriteLock(); 571 | // try { 572 | // _lockDict.Clear(); 573 | // } finally { 574 | // _slimLock.ExitWriteLock(); 575 | // } 576 | // } finally { 577 | // _lock.ExitWriteLock(); 578 | // } 579 | // } 580 | // #endregion 581 | 582 | 583 | // #region private 方法 584 | // private bool tryGet(TKey key, ref TValue value, ref long lastTicks, int secord = 0) 585 | // { 586 | // if (secord == 0) { 587 | // _lock.EnterReadLock(); 588 | // } else { 589 | // _lock.TryEnterReadLock(secord * _thousand); 590 | // } 591 | // try { 592 | // if (_map.TryGetValue(key, out value)) { 593 | // return true; 594 | // } 595 | // lastTicks = _lastTicks; 596 | // } finally { _lock.ExitReadLock(); } 597 | // return false; 598 | // } 599 | 600 | // private bool checkGet(TKey key, long lastTicks, ref TValue value, int secord = 0) 601 | // { 602 | // if (secord == 0) { 603 | // _lock.EnterReadLock(); 604 | // } else { 605 | // _lock.TryEnterReadLock(secord * _thousand); 606 | // } 607 | // try { 608 | // if (_lastTicks != lastTicks && _map.TryGetValue(key, out value)) { 609 | // return true; 610 | // } 611 | // } finally { _lock.ExitReadLock(); } 612 | // return false; 613 | // } 614 | 615 | // private void trySet(TKey key, TValue value, int secord = 0) 616 | // { 617 | // if (secord == 0) { 618 | // _lock.EnterWriteLock(); 619 | // } else { 620 | // _lock.TryEnterWriteLock(secord * _thousand); 621 | // } 622 | // try { 623 | // _map[key] = value; 624 | // } finally { _lock.ExitWriteLock(); } 625 | // } 626 | 627 | // private bool tryRemove(TKey key, ref TValue value, int secord = 0) 628 | // { 629 | // long lastTicks = 0; 630 | // if (tryGet(key, ref value, ref lastTicks, secord)) { 631 | // if (secord == 0) { 632 | // _lock.EnterWriteLock(); 633 | // } else { 634 | // _lock.TryEnterWriteLock(secord * _thousand); 635 | // } 636 | // try { 637 | // _map.Remove(key); 638 | // return true; 639 | // } finally { _lock.ExitWriteLock(); } 640 | // } 641 | // return false; 642 | // } 643 | // private void tryRemove(TKey key, int secord = 0) 644 | // { 645 | // if (secord == 0) { 646 | // _lock.EnterWriteLock(); 647 | // } else { 648 | // _lock.TryEnterWriteLock(secord * _thousand); 649 | // } 650 | // try { 651 | // _map.Remove(key); 652 | // } finally { _lock.ExitWriteLock(); } 653 | // } 654 | 655 | // private bool containsKey(TKey key, int secord = 0) 656 | // { 657 | // if (secord == 0) { 658 | // _lock.EnterReadLock(); 659 | // } else { 660 | // _lock.TryEnterReadLock(secord * _thousand); 661 | // } 662 | // try { 663 | // return _map.ContainsKey(key); 664 | // } finally { _lock.ExitReadLock(); } 665 | // } 666 | 667 | // private AntiDupLockSlim addLock(TKey key) 668 | // { 669 | // AntiDupLockSlim slim; 670 | // _slimLock.EnterWriteLock(); 671 | // try { 672 | // if (_lockDict.TryGetValue(key, out slim) == false) { 673 | // slim = new AntiDupLockSlim(); 674 | // _lockDict[key] = slim; 675 | // } 676 | // slim.UseCount++; 677 | // } finally { _slimLock.ExitWriteLock(); } 678 | // return slim; 679 | // } 680 | 681 | // private void removeLock(TKey key, AntiDupLockSlim slim) 682 | // { 683 | // _slimLock.EnterWriteLock(); 684 | // try { 685 | // slim.UseCount--; 686 | // if (slim.UseCount == 0) { 687 | // _lockDict.Remove(key); 688 | // slim.Dispose(); 689 | // } 690 | // } finally { _slimLock.ExitWriteLock(); } 691 | // } 692 | // #endregion 693 | 694 | 695 | 696 | 697 | 698 | // /// 699 | // /// 执行 700 | // /// 701 | // /// 值 702 | // /// 执行方法 703 | // /// 704 | // public TValue Execute(TKey key, Func factory) 705 | // { 706 | // ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(_rediesConnStr); 707 | // IDatabase db = redis.GetDatabase(_databaseId); 708 | // var keyStr = _prefix + key.ToString(); 709 | // var val = db.StringGet(keyStr); 710 | // if (val.IsNullOrEmpty) { 711 | // if (db.LockTake("lock." + keyStr, "1", TimeSpan.FromSeconds(_timeout * 2))) { 712 | // val = db.StringGet(keyStr); 713 | // if (val.IsNullOrEmpty) { 714 | // var v = factory(); 715 | // var json = JsonConvert.SerializeObject(v); 716 | // db.StringSet(keyStr, json, TimeSpan.FromSeconds(_timeout)); 717 | // db.LockRelease("lock." + keyStr, "1"); 718 | // return v; 719 | // } 720 | // db.LockRelease("lock." + keyStr, "1"); 721 | // } 722 | // } 723 | // if (val.IsNullOrEmpty) { return default(TValue); } 724 | // var str = val.ToString(); 725 | // return JsonConvert.DeserializeObject(str); 726 | // } 727 | 728 | // /// 729 | // /// 执行 730 | // /// 731 | // /// 值 732 | // /// 每次超时秒数,最多8次 733 | // /// 执行方法 734 | // /// 735 | // public TValue Execute(TKey key, int secord, Func factory) 736 | // { 737 | // ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(_rediesConnStr); 738 | // IDatabase db = redis.GetDatabase(_databaseId); 739 | // var keyStr = _prefix + key.ToString(); 740 | // var val = db.StringGet(keyStr); 741 | // if (val.IsNullOrEmpty) { 742 | // if (db.LockTake("lock." + keyStr, "1", TimeSpan.FromSeconds(secord))) { 743 | // val = db.StringGet(keyStr); 744 | // if (val.IsNullOrEmpty) { 745 | // var v = factory(); 746 | // var json = JsonConvert.SerializeObject(v); 747 | // db.StringSet(keyStr, json, TimeSpan.FromSeconds(_timeout)); 748 | // db.LockRelease("lock." + keyStr, "1"); 749 | // return v; 750 | // } 751 | // db.LockRelease("lock." + keyStr, "1"); 752 | // } 753 | // } 754 | // if (val.IsNullOrEmpty) { return default(TValue); } 755 | // var str = val.ToString(); 756 | // return JsonConvert.DeserializeObject(str); 757 | // } 758 | 759 | // /// 760 | // /// 执行 761 | // /// 762 | // /// 值 763 | // /// 执行方法 764 | // /// 765 | // public async Task ExecuteAsync(TKey key, Func> factory) 766 | // { 767 | // ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(_rediesConnStr); 768 | // IDatabase db = redis.GetDatabase(_databaseId); 769 | // var keyStr = _prefix + key.ToString(); 770 | // var val = await db.StringGetAsync(keyStr); 771 | // if (val.IsNullOrEmpty) { 772 | // if (await db.LockTakeAsync("lock." + keyStr, "1", TimeSpan.FromSeconds(_timeout * 2))) { 773 | // val = await db.StringGetAsync(keyStr); 774 | // if (val.IsNullOrEmpty) { 775 | // var v = await factory(); 776 | // var json = JsonConvert.SerializeObject(v); 777 | // await db.StringSetAsync(keyStr, json, TimeSpan.FromSeconds(_timeout)); 778 | // await db.LockReleaseAsync("lock." + keyStr, "1"); 779 | // return v; 780 | // } 781 | // await db.LockReleaseAsync("lock." + keyStr, "1"); 782 | // } 783 | // } 784 | // if (val.IsNullOrEmpty) { return default(TValue); } 785 | // var str = val.ToString(); 786 | // return JsonConvert.DeserializeObject(str); 787 | // } 788 | 789 | // /// 790 | // /// 执行 791 | // /// 792 | // /// 值 793 | // /// 每次超时秒数,最多8次 794 | // /// 执行方法 795 | // /// 796 | // public async Task ExecuteAsync(TKey key, int secord, Func> factory) 797 | // { 798 | // ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(_rediesConnStr); 799 | // IDatabase db = redis.GetDatabase(_databaseId); 800 | // var keyStr = _prefix + key.ToString(); 801 | // var val = await db.StringGetAsync(keyStr); 802 | // if (val.IsNullOrEmpty) { 803 | // if (await db.LockTakeAsync("lock." + keyStr, "1", TimeSpan.FromSeconds(_timeout * 2))) { 804 | // val = await db.StringGetAsync(keyStr); 805 | // if (val.IsNullOrEmpty) { 806 | // var v = await factory(); 807 | // var json = JsonConvert.SerializeObject(v); 808 | // await db.StringSetAsync(keyStr, json, TimeSpan.FromSeconds(_timeout)); 809 | // await db.LockReleaseAsync("lock." + keyStr, "1"); 810 | // return v; 811 | // } 812 | // await db.LockReleaseAsync("lock." + keyStr, "1"); 813 | // } 814 | // } 815 | // if (val.IsNullOrEmpty) { return default(TValue); } 816 | // var str = val.ToString(); 817 | // return JsonConvert.DeserializeObject(str); 818 | // } 819 | 820 | // /// 821 | // /// 移除KEY 822 | // /// 823 | // /// 824 | // public void Remove(TKey key) 825 | // { 826 | // ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(_rediesConnStr); 827 | // IDatabase db = redis.GetDatabase(_databaseId); 828 | // var keyStr = _prefix + key.ToString(); 829 | // db.KeyDelete(keyStr); 830 | // } 831 | 832 | // } 833 | //} 834 | -------------------------------------------------------------------------------- /ToolGood.AntiDuplication.Redis.StackExchange/ToolGood.AntiDuplication.Redis.StackExchange.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net45;netstandard2.0; 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 1.2.6 18 | 19 | 20 | 21 | 22 | 23 | 2.0.601 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /ToolGood.AntiDuplication.Test/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ToolGood.AntiDuplication.Test/Cache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace ToolGood.AntiDuplication.QueryApi 9 | { 10 | internal class Cache 11 | { 12 | private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); 13 | private readonly Dictionary _map = new Dictionary(); 14 | 15 | public int Count { 16 | get { return _map.Count; } 17 | } 18 | 19 | public TValue Execute(TKey key, Func factory) 20 | { 21 | // Check cache 22 | _lock.EnterReadLock(); 23 | TValue val; 24 | try { 25 | if (_map.TryGetValue(key, out val)) 26 | return val; 27 | } finally { 28 | _lock.ExitReadLock(); 29 | } 30 | 31 | // Cache it 32 | _lock.EnterWriteLock(); 33 | try { 34 | // Check again 35 | if (_map.TryGetValue(key, out val)) 36 | return val; 37 | 38 | // Create it 39 | val = factory(); 40 | 41 | // Store it 42 | _map.Add(key, val); 43 | 44 | // Done 45 | return val; 46 | } finally { 47 | _lock.ExitWriteLock(); 48 | } 49 | } 50 | 51 | public void Clear() 52 | { 53 | // Cache it 54 | _lock.EnterWriteLock(); 55 | try { 56 | _map.Clear(); 57 | } finally { 58 | _lock.ExitWriteLock(); 59 | } 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /ToolGood.AntiDuplication.Test/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using ToolGood.AntiDuplication.QueryApi; 9 | 10 | namespace ToolGood.AntiDuplication.Test 11 | { 12 | class Program 13 | { 14 | private readonly static AntiDupCache antiDupCache = new AntiDupCache(50, 1); 15 | private readonly static AntiDupQueue antiDupQueue = new AntiDupQueue(50); 16 | private readonly static DictCache dictCache = new DictCache(); 17 | private readonly static Cache cache = new Cache(); 18 | 19 | 20 | static void Main(string[] args) 21 | { 22 | var processorCount = Environment.ProcessorCount; 23 | Test2(10000, processorCount); 24 | 25 | Test3(1000, processorCount); 26 | 27 | for (int count = 1; count <= processorCount; count++) { 28 | Test(count, processorCount); 29 | } 30 | 31 | //antiDupQueue.Execute(1, () => { 32 | // var task = Task.Run(() => { return 1; }); 33 | // var val = await task; 34 | // return val; 35 | //}); 36 | 37 | 38 | Console.WriteLine("----------------------- 结束 -----------------------"); 39 | Console.ReadLine(); 40 | } 41 | 42 | 43 | 44 | 45 | private static void Test(int count, int lism) 46 | { 47 | var list = Build(count); 48 | antiDupCache.Clear(); 49 | antiDupQueue.Clear(); 50 | dictCache.Clear(); 51 | cache.Clear(); 52 | Console.WriteLine($"----------------------- 开始 从1到100 重复次数:{count} 单位: ms -----------------------"); 53 | Console.Write(" 并发数量: "); 54 | for (int i = 1; i <= lism; i++) { 55 | Console.Write(i.ToString().PadRight(5)); 56 | } 57 | Console.Write("\r\n"); 58 | 59 | var stopwatch = Stopwatch.StartNew(); 60 | Console.Write(" 普通并发:"); 61 | for (int i = 1; i <= lism; i++) { 62 | stopwatch = Stopwatch.StartNew(); 63 | Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = i }, (j) => { 64 | Thread.Sleep(1); 65 | }); 66 | stopwatch.Stop(); 67 | Console.Write(" " + stopwatch.ElapsedMilliseconds.ToString().PadRight(4)); 68 | } 69 | Console.Write("\r\n"); 70 | 71 | 72 | Console.Write(" AntiDupCache:"); 73 | for (int i = 1; i <= lism; i++) { 74 | antiDupCache.Clear(); 75 | stopwatch = Stopwatch.StartNew(); 76 | Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = i }, (j) => { 77 | antiDupCache.GetOrAdd(j, () => { 78 | Thread.Sleep(1); 79 | return j; 80 | }); 81 | }); 82 | stopwatch.Stop(); 83 | Console.Write(" " + stopwatch.ElapsedMilliseconds.ToString().PadRight(4)); 84 | } 85 | Console.Write("\r\n"); 86 | 87 | Console.Write(" AntiDupQueue:"); 88 | for (int i = 1; i <= lism; i++) { 89 | antiDupQueue.Clear(); 90 | stopwatch = Stopwatch.StartNew(); 91 | Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = i }, (j) => { 92 | antiDupQueue.GetOrAdd(j, () => { 93 | Thread.Sleep(1); 94 | return j; 95 | }); 96 | }); 97 | stopwatch.Stop(); 98 | Console.Write(" " + stopwatch.ElapsedMilliseconds.ToString().PadRight(4)); 99 | } 100 | Console.Write("\r\n"); 101 | 102 | Console.Write(" DictCache:"); 103 | for (int i = 1; i <= lism; i++) { 104 | dictCache.Clear(); 105 | stopwatch = Stopwatch.StartNew(); 106 | Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = i }, (j) => { 107 | dictCache.GetOrAdd(j, () => { 108 | Thread.Sleep(1); 109 | return j; 110 | }); 111 | }); 112 | stopwatch.Stop(); 113 | Console.Write(" " + stopwatch.ElapsedMilliseconds.ToString().PadRight(4)); 114 | } 115 | Console.Write("\r\n"); 116 | 117 | Console.Write(" Cache:"); 118 | for (int i = 1; i <= lism; i++) { 119 | cache.Clear(); 120 | stopwatch = Stopwatch.StartNew(); 121 | Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = i }, (j) => { 122 | cache.Execute(j, () => { 123 | Thread.Sleep(1); 124 | return j; 125 | }); 126 | }); 127 | stopwatch.Stop(); 128 | Console.Write(" " + stopwatch.ElapsedMilliseconds.ToString().PadRight(4)); 129 | } 130 | Console.Write("\r\n"); 131 | 132 | 133 | stopwatch = Stopwatch.StartNew(); 134 | Console.Write("第二次普通并发:"); 135 | for (int i = 1; i <= lism; i++) { 136 | stopwatch = Stopwatch.StartNew(); 137 | Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = i }, (j) => { 138 | Thread.Sleep(1); 139 | }); 140 | stopwatch.Stop(); 141 | Console.Write(" " + stopwatch.ElapsedMilliseconds.ToString().PadRight(4)); 142 | } 143 | Console.Write("\r\n"); 144 | Console.Write("\r\n"); 145 | 146 | } 147 | 148 | private static void Test2(int count, int lism) 149 | { 150 | var list = Build(count); 151 | Console.WriteLine($"----------------------- 测试缓存性能 从1到100 重复次数:{count} 单位: ms -----------------------"); 152 | Console.Write(" 并发数量: "); 153 | for (int i = 1; i <= lism; i++) { 154 | Console.Write(i.ToString().PadRight(5)); 155 | } 156 | Console.Write("\r\n"); 157 | 158 | var stopwatch = Stopwatch.StartNew(); 159 | Console.Write("AntiDupCache:"); 160 | for (int i = 1; i <= lism; i++) { 161 | antiDupCache.Clear(); 162 | stopwatch = Stopwatch.StartNew(); 163 | Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = i }, (j) => { 164 | antiDupCache.GetOrAdd(j, () => { 165 | return j; 166 | }); 167 | }); 168 | stopwatch.Stop(); 169 | Console.Write(" " + stopwatch.ElapsedMilliseconds.ToString().PadRight(4)); 170 | } 171 | Console.Write("\r\n"); 172 | 173 | Console.Write("AntiDupQueue:"); 174 | for (int i = 1; i <= lism; i++) { 175 | antiDupQueue.Clear(); 176 | stopwatch = Stopwatch.StartNew(); 177 | Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = i }, (j) => { 178 | antiDupQueue.GetOrAdd(j, () => { 179 | return j; 180 | }); 181 | }); 182 | stopwatch.Stop(); 183 | Console.Write(" " + stopwatch.ElapsedMilliseconds.ToString().PadRight(4)); 184 | } 185 | Console.Write("\r\n"); 186 | 187 | Console.Write(" DictCache:"); 188 | for (int i = 1; i <= lism; i++) { 189 | dictCache.Clear(); 190 | stopwatch = Stopwatch.StartNew(); 191 | Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = i }, (j) => { 192 | dictCache.GetOrAdd(j, () => { 193 | return j; 194 | }); 195 | }); 196 | stopwatch.Stop(); 197 | Console.Write(" " + stopwatch.ElapsedMilliseconds.ToString().PadRight(4)); 198 | } 199 | Console.Write("\r\n"); 200 | 201 | Console.Write(" Cache:"); 202 | for (int i = 1; i <= lism; i++) { 203 | cache.Clear(); 204 | stopwatch = Stopwatch.StartNew(); 205 | Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = i }, (j) => { 206 | cache.Execute(j, () => { 207 | return j; 208 | }); 209 | }); 210 | stopwatch.Stop(); 211 | Console.Write(" " + stopwatch.ElapsedMilliseconds.ToString().PadRight(4)); 212 | } 213 | Console.Write("\r\n"); 214 | Console.Write("\r\n"); 215 | 216 | } 217 | 218 | private static void Test3(int count, int lism) 219 | { 220 | var list = Build2(count); 221 | antiDupCache.Clear(); 222 | antiDupQueue.Clear(); 223 | dictCache.Clear(); 224 | cache.Clear(); 225 | Console.WriteLine($"----------------------- 仿线上环境 从1到{count} 单位: ms -----------------------"); 226 | Console.Write(" 并发数量: "); 227 | for (int i = 1; i <= lism; i++) { 228 | Console.Write(i.ToString().PadRight(5)); 229 | } 230 | Console.Write("\r\n"); 231 | 232 | var stopwatch = Stopwatch.StartNew(); 233 | Console.Write(" 普通并发:"); 234 | for (int i = 1; i <= lism; i++) { 235 | stopwatch = Stopwatch.StartNew(); 236 | Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = i }, (j) => { 237 | Thread.Sleep(1); 238 | }); 239 | stopwatch.Stop(); 240 | Console.Write(" " + stopwatch.ElapsedMilliseconds.ToString().PadRight(4)); 241 | } 242 | Console.Write("\r\n"); 243 | 244 | 245 | Console.Write(" AntiDupCache:"); 246 | for (int i = 1; i <= lism; i++) { 247 | antiDupCache.Clear(); 248 | stopwatch = Stopwatch.StartNew(); 249 | Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = i }, (j) => { 250 | antiDupCache.GetOrAdd(j, () => { 251 | Thread.Sleep(1); 252 | return j; 253 | }); 254 | }); 255 | stopwatch.Stop(); 256 | Console.Write(" " + stopwatch.ElapsedMilliseconds.ToString().PadRight(4)); 257 | } 258 | Console.Write("\r\n"); 259 | 260 | Console.Write(" AntiDupQueue:"); 261 | for (int i = 1; i <= lism; i++) { 262 | antiDupQueue.Clear(); 263 | stopwatch = Stopwatch.StartNew(); 264 | Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = i }, (j) => { 265 | antiDupQueue.GetOrAdd(j, () => { 266 | Thread.Sleep(1); 267 | return j; 268 | }); 269 | }); 270 | stopwatch.Stop(); 271 | Console.Write(" " + stopwatch.ElapsedMilliseconds.ToString().PadRight(4)); 272 | } 273 | Console.Write("\r\n"); 274 | 275 | Console.Write(" DictCache:"); 276 | for (int i = 1; i <= lism; i++) { 277 | dictCache.Clear(); 278 | stopwatch = Stopwatch.StartNew(); 279 | Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = i }, (j) => { 280 | dictCache.GetOrAdd(j, () => { 281 | Thread.Sleep(1); 282 | return j; 283 | }); 284 | }); 285 | stopwatch.Stop(); 286 | Console.Write(" " + stopwatch.ElapsedMilliseconds.ToString().PadRight(4)); 287 | } 288 | Console.Write("\r\n"); 289 | 290 | Console.Write(" Cache:"); 291 | for (int i = 1; i <= lism; i++) { 292 | cache.Clear(); 293 | stopwatch = Stopwatch.StartNew(); 294 | Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = i }, (j) => { 295 | cache.Execute(j, () => { 296 | Thread.Sleep(1); 297 | return j; 298 | }); 299 | }); 300 | stopwatch.Stop(); 301 | Console.Write(" " + stopwatch.ElapsedMilliseconds.ToString().PadRight(4)); 302 | } 303 | Console.Write("\r\n"); 304 | 305 | 306 | stopwatch = Stopwatch.StartNew(); 307 | Console.Write("第二次普通并发:"); 308 | for (int i = 1; i <= lism; i++) { 309 | stopwatch = Stopwatch.StartNew(); 310 | Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = i }, (j) => { 311 | Thread.Sleep(1); 312 | }); 313 | stopwatch.Stop(); 314 | Console.Write(" " + stopwatch.ElapsedMilliseconds.ToString().PadRight(4)); 315 | } 316 | Console.Write("\r\n"); 317 | Console.Write("\r\n"); 318 | 319 | } 320 | 321 | 322 | private static List Build(int count) 323 | { 324 | List list = new List(); 325 | for (int i = 0; i < 100; i++) { 326 | for (int j = 0; j < count; j++) { 327 | list.Add(i); 328 | } 329 | } 330 | return list; 331 | } 332 | 333 | private static List Build2(int count) 334 | { 335 | Random random = new Random(); 336 | 337 | List list = new List(); 338 | while (true) { 339 | for (int i = 0; i < count; i++) { 340 | list.Add(i); 341 | if (random.NextDouble() > 0.99) { 342 | list.Add(i); 343 | } 344 | } 345 | if (list.Count- count > 10) { 346 | return list; 347 | } 348 | list.Clear(); 349 | } 350 | } 351 | 352 | } 353 | 354 | } 355 | -------------------------------------------------------------------------------- /ToolGood.AntiDuplication.Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("ToolGood.AntiDuplication.Test")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("HP Inc.")] 12 | [assembly: AssemblyProduct("ToolGood.AntiDuplication.Test")] 13 | [assembly: AssemblyCopyright("Copyright © HP Inc. 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("8f61f9e8-0976-49f8-8d44-f228ee2cfdc0")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 33 | // 方法是按如下所示使用“*”: : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /ToolGood.AntiDuplication.Test/ToolGood.AntiDuplication.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {8F61F9E8-0976-49F8-8D44-F228EE2CFDC0} 8 | Exe 9 | ToolGood.AntiDuplication.Test 10 | ToolGood.AntiDuplication.Test 11 | v4.6.1 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | {7cc9dd8f-3077-40bf-a144-35ad4f685b4e} 56 | ToolGood.AntiDuplication 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /ToolGood.AntiDuplication.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.329 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ToolGood.AntiDuplication", "ToolGood.AntiDuplication\ToolGood.AntiDuplication.csproj", "{7CC9DD8F-3077-40BF-A144-35AD4F685B4E}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ToolGood.AntiDuplication.Test", "ToolGood.AntiDuplication.Test\ToolGood.AntiDuplication.Test.csproj", "{8F61F9E8-0976-49F8-8D44-F228EE2CFDC0}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ToolGood.AntiDuplication.Redis.StackExchange", "ToolGood.AntiDuplication.Redis.StackExchange\ToolGood.AntiDuplication.Redis.StackExchange.csproj", "{FCFBC3E7-5E3E-48EF-80F7-329CF3C8223E}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {7CC9DD8F-3077-40BF-A144-35AD4F685B4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {7CC9DD8F-3077-40BF-A144-35AD4F685B4E}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {7CC9DD8F-3077-40BF-A144-35AD4F685B4E}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {7CC9DD8F-3077-40BF-A144-35AD4F685B4E}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {8F61F9E8-0976-49F8-8D44-F228EE2CFDC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {8F61F9E8-0976-49F8-8D44-F228EE2CFDC0}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {8F61F9E8-0976-49F8-8D44-F228EE2CFDC0}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {8F61F9E8-0976-49F8-8D44-F228EE2CFDC0}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {FCFBC3E7-5E3E-48EF-80F7-329CF3C8223E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {FCFBC3E7-5E3E-48EF-80F7-329CF3C8223E}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {FCFBC3E7-5E3E-48EF-80F7-329CF3C8223E}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {FCFBC3E7-5E3E-48EF-80F7-329CF3C8223E}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {495C7FE9-B836-4348-96E3-E9601AFEDD0B} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /ToolGood.AntiDuplication/AntiDupCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Concurrent; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace ToolGood.AntiDuplication 8 | { 9 | /// 10 | /// 防重复缓存 11 | /// 12 | /// 13 | /// 14 | public class AntiDupCache : IConcurrentCache 15 | { 16 | private const int _thousand = 1000; 17 | private readonly int _maxCount;//缓存最高数量 18 | private readonly long _expireTicks;//超时 Ticks 19 | private long _lastTicks;//最后Ticks 20 | private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); 21 | private readonly ReaderWriterLockSlim _slimLock = new ReaderWriterLockSlim(); 22 | private readonly Dictionary> _map = new Dictionary>(); 23 | private readonly Dictionary _lockDict = new Dictionary(); 24 | private readonly Queue _queue = new Queue(); 25 | class AntiDupLockSlim : ReaderWriterLockSlim { public int UseCount; } 26 | 27 | /// 28 | /// 防重复缓存 29 | /// 30 | /// 缓存最高数量,0 不缓存,-1 缓存所有 31 | /// 超时秒数,0 不缓存,-1 永久缓存 32 | public AntiDupCache(int maxCount = 100, int expireSecond = 1) 33 | { 34 | if (maxCount < 0) { 35 | _maxCount = -1; 36 | } else { 37 | _maxCount = maxCount; 38 | } 39 | if (expireSecond < 0) { 40 | _expireTicks = -1; 41 | } else { 42 | _expireTicks = expireSecond * TimeSpan.FromSeconds(1).Ticks; 43 | } 44 | } 45 | 46 | 47 | #region 属性 48 | /// 49 | /// 获取缓存个数 50 | /// 51 | public int Count { 52 | get { 53 | _lock.EnterReadLock(); 54 | try { 55 | return _map.Count; 56 | } finally { _lock.ExitReadLock(); } 57 | } 58 | } 59 | 60 | /// 61 | /// 是否为空 62 | /// 63 | public bool IsEmpty { 64 | get { 65 | _lock.EnterReadLock(); 66 | try { 67 | return _map.Count == 0; 68 | } finally { _lock.ExitReadLock(); } 69 | 70 | } 71 | } 72 | #endregion 73 | 74 | #region TValue 75 | 76 | /// 77 | /// 获取或添加缓存 78 | /// 79 | /// 关键字 80 | /// 每次超时秒数 81 | /// 值 82 | /// 83 | public TValue GetOrAdd(TKey key, TValue val, int secord = 0) 84 | { 85 | // 过期时间为0 则不缓存 86 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return val; } 87 | 88 | TValue value = default; 89 | long lastTicks = 0; 90 | if (tryGet(key, ref value, ref lastTicks, secord)) { return value; } 91 | 92 | AntiDupLockSlim slim; 93 | _slimLock.EnterUpgradeableReadLock(); 94 | try { 95 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 96 | slim = addLock(key); 97 | } finally { _slimLock.ExitUpgradeableReadLock(); } 98 | 99 | slim.EnterWriteLock(); 100 | try { 101 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 102 | trySet(key, val, secord); 103 | return val; 104 | } finally { 105 | slim.ExitWriteLock(); 106 | removeLock(key, slim); 107 | } 108 | } 109 | 110 | /// 111 | /// 设置缓存 112 | /// 113 | /// 关键字 114 | /// 每次超时秒数 115 | /// 值 116 | public void SetValue(TKey key, TValue val, int secord = 0) 117 | { 118 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) return; 119 | trySet(key, val, secord); 120 | } 121 | 122 | /// 123 | /// 添加或更新缓存 124 | /// 125 | /// 关键字 126 | /// 添加值 127 | /// 更新值 128 | /// 每次超时秒数 129 | /// 130 | public TValue AddOrUpdate(TKey key, TValue addValue, TValue updateValue, int secord = 0) 131 | { 132 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return addValue; } 133 | 134 | TValue value = default; 135 | long lastTicks = 0; 136 | if (tryGet(key, ref value, ref lastTicks, secord)) { 137 | trySet(key, updateValue); 138 | return value; 139 | } 140 | 141 | AntiDupLockSlim slim; 142 | _slimLock.EnterUpgradeableReadLock(); 143 | try { 144 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 145 | slim = addLock(key); 146 | } finally { _slimLock.ExitUpgradeableReadLock(); } 147 | 148 | slim.EnterWriteLock(); 149 | try { 150 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 151 | trySet(key, addValue, secord); 152 | return addValue; 153 | } finally { 154 | slim.ExitWriteLock(); 155 | removeLock(key, slim); 156 | } 157 | } 158 | 159 | /// 160 | /// 尝试获取缓存 161 | /// 162 | /// 关键字 163 | /// 每次超时秒数 164 | /// 值 165 | /// 166 | public bool TryGetValue(TKey key, out TValue val, int secord = 0) 167 | { 168 | val = default; 169 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return false; } 170 | 171 | long lastTicks = 0; 172 | if (tryGet(key, ref val, ref lastTicks, secord)) { return true; } 173 | return false; 174 | } 175 | 176 | /// 177 | /// 尝试添加缓存 178 | /// 179 | /// 关键字 180 | /// 值 181 | /// 每次超时秒数 182 | /// 183 | public bool TryAdd(TKey key, TValue val, int secord = 0) 184 | { 185 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return false; } 186 | 187 | long lastTicks = 0; 188 | TValue value = default; 189 | if (tryGet(key, ref value, ref lastTicks, secord) == false) { 190 | trySet(key, val, secord); 191 | return true; 192 | } 193 | return false; 194 | } 195 | /// 196 | /// 尝试更新缓存 197 | /// 198 | /// 关键字 199 | /// 新值 200 | /// 每次超时秒数 201 | /// 202 | public bool TryUpdate(TKey key, TValue newValue, int secord = 0) 203 | { 204 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return false; } 205 | 206 | TValue value = default; 207 | long lastTicks = 0; 208 | if (tryGet(key, ref value, ref lastTicks, secord)) { 209 | trySet(key, newValue); 210 | return true; 211 | } 212 | return false; 213 | } 214 | /// 215 | /// 尝试更新缓存 216 | /// 217 | /// 关键字 218 | /// 新值 219 | /// 原值 220 | /// 每次超时秒数 221 | /// 222 | public bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue, int secord = 0) 223 | { 224 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return false; } 225 | 226 | TValue value = default; 227 | long lastTicks = 0; 228 | if (tryGet(key, ref value, ref lastTicks, secord)) { 229 | if (object.Equals(value, comparisonValue)) { 230 | trySet(key, newValue); 231 | } 232 | return true; 233 | } 234 | return false; 235 | } 236 | /// 237 | /// 尝试删除缓存 238 | /// 239 | /// 关键字 240 | /// 值 241 | /// 每次超时秒数 242 | /// 243 | public bool TryRemove(TKey key, out TValue value, int secord = 0) 244 | { 245 | value = default; 246 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return false; } 247 | return tryRemove(key, ref value, secord); 248 | } 249 | /// 250 | /// 删除缓存 251 | /// 252 | /// 关键字 253 | /// 每次超时秒数 254 | public void Remove(TKey key, int secord = 0) 255 | { 256 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return; } 257 | tryRemove(key, secord); 258 | } 259 | /// 260 | /// 是否包含缓存 261 | /// 262 | /// 263 | /// 264 | /// 265 | public bool ContainsKey(TKey key, int secord = 0) 266 | { 267 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return false; } 268 | 269 | return containsKey(key, secord); 270 | } 271 | 272 | #endregion 273 | 274 | #region Func factory 275 | /// 276 | /// 获取或添加缓存 277 | /// 278 | /// 关键字 279 | /// 每次超时秒数 280 | /// 执行方法 281 | /// 282 | public TValue GetOrAdd(TKey key, Func factory, int secord = 0) 283 | { 284 | // 过期时间为0 则不缓存 285 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return factory(); } 286 | 287 | TValue value = default; 288 | long lastTicks = 0; 289 | if (tryGet(key, ref value, ref lastTicks, secord)) { return value; } 290 | 291 | AntiDupLockSlim slim; 292 | _slimLock.EnterUpgradeableReadLock(); 293 | try { 294 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 295 | slim = addLock(key); 296 | } finally { _slimLock.ExitUpgradeableReadLock(); } 297 | 298 | slim.EnterWriteLock(); 299 | try { 300 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 301 | var val = factory(); 302 | trySet(key, val, secord); 303 | return val; 304 | } finally { 305 | slim.ExitWriteLock(); 306 | removeLock(key, slim); 307 | } 308 | } 309 | /// 310 | /// 设置缓存 311 | /// 312 | /// 关键字 313 | /// 执行方法 314 | /// 每次超时秒数 315 | /// 316 | public TValue SetValue(TKey key, Func factory, int secord = 0) 317 | { 318 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) return factory(); 319 | var value = factory(); 320 | trySet(key, value, secord); 321 | return value; 322 | } 323 | /// 324 | /// 添加或更新缓存 325 | /// 326 | /// 关键字 327 | /// 添加的值 328 | /// 更新的值 329 | /// 每次超时秒数 330 | /// 331 | public TValue AddOrUpdate(TKey key, Func addValueFactory, Func updateValueFactory, int secord) 332 | { 333 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return addValueFactory(); } 334 | 335 | TValue value = default; 336 | long lastTicks = 0; 337 | if (tryGet(key, ref value, ref lastTicks, secord)) { 338 | value = updateValueFactory(); 339 | trySet(key, value); 340 | return value; 341 | } 342 | 343 | AntiDupLockSlim slim; 344 | _slimLock.EnterUpgradeableReadLock(); 345 | try { 346 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 347 | slim = addLock(key); 348 | } finally { _slimLock.ExitUpgradeableReadLock(); } 349 | 350 | slim.EnterWriteLock(); 351 | try { 352 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 353 | var val = addValueFactory(); 354 | trySet(key, val, secord); 355 | return val; 356 | } finally { 357 | slim.ExitWriteLock(); 358 | removeLock(key, slim); 359 | } 360 | } 361 | /// 362 | /// 尝试添加缓存 363 | /// 364 | /// 关键字 365 | /// 366 | /// 每次超时秒数 367 | /// 368 | public bool TryAdd(TKey key, Func factory, int secord = 0) 369 | { 370 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return false; } 371 | var value = factory(); 372 | trySet(key, value, secord); 373 | return true; 374 | } 375 | /// 376 | /// 尝试更新缓存 377 | /// 378 | /// 关键字 379 | /// 更新的值 380 | /// 每次超时秒数 381 | /// 382 | public bool TryUpdate(TKey key, Func updateValueFactory, int secord = 0) 383 | { 384 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return false; } 385 | 386 | TValue value = default; 387 | long lastTicks = 0; 388 | if (tryGet(key, ref value, ref lastTicks, secord)) { 389 | trySet(key, updateValueFactory(), secord); 390 | return true; 391 | } 392 | return false; 393 | } 394 | /// 395 | /// 尝试更新缓存 396 | /// 397 | /// 关键字 398 | /// 更新的值 399 | /// 原值 400 | /// 每次超时秒数 401 | /// 402 | public bool TryUpdate(TKey key, Func updateValueFactory, TValue comparisonValue, int secord = 0) 403 | { 404 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return false; } 405 | 406 | TValue value = default; 407 | long lastTicks = 0; 408 | if (tryGet(key, ref value, ref lastTicks, secord)) { 409 | if (object.Equals(value, comparisonValue)) { 410 | trySet(key, updateValueFactory(), secord); 411 | } 412 | return true; 413 | } 414 | return false; 415 | } 416 | 417 | #endregion 418 | 419 | #region async Func factory 420 | #if !NET40 421 | /// 422 | /// 获取或添加缓存 423 | /// 424 | /// 关键字 425 | /// 每次超时秒数 426 | /// 执行方法 427 | /// 428 | public async Task GetOrAdd(TKey key, Func> factory, int secord = 0) 429 | { 430 | // 过期时间为0 则不缓存 431 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return await factory(); } 432 | 433 | TValue value = default; 434 | long lastTicks = 0; 435 | if (tryGet(key, ref value, ref lastTicks, secord)) { return value; } 436 | 437 | AntiDupLockSlim slim; 438 | _slimLock.EnterUpgradeableReadLock(); 439 | try { 440 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 441 | slim = addLock(key); 442 | } finally { _slimLock.ExitUpgradeableReadLock(); } 443 | 444 | slim.EnterWriteLock(); 445 | try { 446 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 447 | var val = await factory(); 448 | trySet(key, val, secord); 449 | return val; 450 | } finally { 451 | slim.ExitWriteLock(); 452 | removeLock(key, slim); 453 | } 454 | } 455 | /// 456 | /// 设置缓存 457 | /// 458 | /// 关键字 459 | /// 执行方法 460 | /// 每次超时秒数 461 | /// 462 | public async Task SetValue(TKey key, Func> factory, int secord = 0) 463 | { 464 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) return await factory(); 465 | var value = await factory(); 466 | trySet(key, value, secord); 467 | return value; 468 | } 469 | /// 470 | /// 添加或更新缓存 471 | /// 472 | /// 关键字 473 | /// 添加的值 474 | /// 更新的值 475 | /// 每次超时秒数 476 | /// 477 | public async Task AddOrUpdate(TKey key, Func> addValueFactory, Func> updateValueFactory, int secord) 478 | { 479 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return await addValueFactory(); } 480 | 481 | TValue value = default; 482 | long lastTicks = 0; 483 | if (tryGet(key, ref value, ref lastTicks, secord)) { 484 | value = await updateValueFactory(); 485 | trySet(key, value); 486 | return value; 487 | } 488 | 489 | AntiDupLockSlim slim; 490 | _slimLock.EnterUpgradeableReadLock(); 491 | try { 492 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 493 | slim = addLock(key); 494 | } finally { _slimLock.ExitUpgradeableReadLock(); } 495 | 496 | slim.EnterWriteLock(); 497 | try { 498 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 499 | var val = await addValueFactory(); 500 | trySet(key, val, secord); 501 | return val; 502 | } finally { 503 | slim.ExitWriteLock(); 504 | removeLock(key, slim); 505 | } 506 | } 507 | /// 508 | /// 尝试添加缓存 509 | /// 510 | /// 关键字 511 | /// 512 | /// 每次超时秒数 513 | /// 514 | public async Task TryAdd(TKey key, Func> factory, int secord = 0) 515 | { 516 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return false; } 517 | var value = await factory(); 518 | trySet(key, value, secord); 519 | return true; 520 | } 521 | /// 522 | /// 尝试更新缓存 523 | /// 524 | /// 关键字 525 | /// 更新的值 526 | /// 每次超时秒数 527 | /// 528 | public async Task TryUpdate(TKey key, Func> updateValueFactory, int secord = 0) 529 | { 530 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return false; } 531 | 532 | TValue value = default; 533 | long lastTicks = 0; 534 | if (tryGet(key, ref value, ref lastTicks, secord)) { 535 | trySet(key, await updateValueFactory(), secord); 536 | return true; 537 | } 538 | return false; 539 | } 540 | /// 541 | /// 尝试更新缓存 542 | /// 543 | /// 关键字 544 | /// 更新的值 545 | /// 原值 546 | /// 每次超时秒数 547 | /// 548 | public async Task TryUpdate(TKey key, Func> updateValueFactory, TValue comparisonValue, int secord = 0) 549 | { 550 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return false; } 551 | 552 | TValue value = default; 553 | long lastTicks = 0; 554 | if (tryGet(key, ref value, ref lastTicks, secord)) { 555 | if (object.Equals(value, comparisonValue)) { 556 | trySet(key, await updateValueFactory(), secord); 557 | } 558 | return true; 559 | } 560 | return false; 561 | } 562 | /// 563 | /// 尝试更新缓存 564 | /// 565 | /// 关键字 566 | /// 更新的值 567 | /// 原值 568 | /// 每次超时秒数 569 | /// 570 | public async Task TryUpdate(TKey key, Func> updateValueFactory, Func> comparisonValueFactory, int secord = 0) 571 | { 572 | if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return false; } 573 | 574 | TValue value = default; 575 | long lastTicks = 0; 576 | if (tryGet(key, ref value, ref lastTicks, secord)) { 577 | if (object.Equals(value, await comparisonValueFactory())) { 578 | trySet(key, await updateValueFactory(), secord); 579 | } 580 | return true; 581 | } 582 | return false; 583 | } 584 | 585 | #endif 586 | #endregion 587 | 588 | #region 清空 589 | /// 590 | /// 清空 591 | /// 592 | public void Clear() 593 | { 594 | _lock.EnterWriteLock(); 595 | try { 596 | _map.Clear(); 597 | _queue.Clear(); 598 | _slimLock.EnterWriteLock(); 599 | try { 600 | _lockDict.Clear(); 601 | } finally { 602 | _slimLock.ExitWriteLock(); 603 | } 604 | } finally { 605 | _lock.ExitWriteLock(); 606 | } 607 | } 608 | #endregion 609 | 610 | 611 | #region private 方法 612 | private bool tryGet(TKey key, ref TValue value, ref long lastTicks, int secord = 0) 613 | { 614 | Tuple tuple; 615 | if (secord == 0) { 616 | _lock.EnterReadLock(); 617 | } else { 618 | _lock.TryEnterReadLock(secord * _thousand); 619 | } 620 | try { 621 | if (_map.TryGetValue(key, out tuple)) { 622 | if (_expireTicks == -1) { 623 | value = tuple.Item2; 624 | return true; 625 | } 626 | if (tuple.Item1 + _expireTicks > DateTime.Now.Ticks) { 627 | value = tuple.Item2; 628 | return true; 629 | } 630 | } 631 | lastTicks = _lastTicks; 632 | } finally { _lock.ExitReadLock(); } 633 | return false; 634 | } 635 | 636 | private bool checkGet(TKey key, long lastTicks, ref TValue value, int secord = 0) 637 | { 638 | Tuple tuple; 639 | if (secord == 0) { 640 | _lock.EnterReadLock(); 641 | } else { 642 | _lock.TryEnterReadLock(secord * _thousand); 643 | } 644 | try { 645 | if (_lastTicks != lastTicks && _map.TryGetValue(key, out tuple)) { 646 | if (_expireTicks == -1) { 647 | value = tuple.Item2; 648 | return true; 649 | } 650 | if (tuple.Item1 + _expireTicks > DateTime.Now.Ticks) { 651 | value = tuple.Item2; 652 | return true; 653 | } 654 | } 655 | } finally { _lock.ExitReadLock(); } 656 | return false; 657 | } 658 | 659 | private void trySet(TKey key, TValue value, int secord = 0) 660 | { 661 | if (secord == 0) { 662 | _lock.EnterWriteLock(); 663 | } else { 664 | _lock.TryEnterWriteLock(secord * _thousand); 665 | } 666 | try { 667 | _lastTicks = DateTime.Now.Ticks; 668 | _map[key] = Tuple.Create(_lastTicks, value); 669 | if (_maxCount > 0) { 670 | if (_queue.Contains(key) == false) { 671 | _queue.Enqueue(key); 672 | if (_queue.Count > _maxCount) _map.Remove(_queue.Dequeue()); 673 | } 674 | } 675 | } finally { _lock.ExitWriteLock(); } 676 | } 677 | 678 | private bool tryRemove(TKey key, ref TValue value, int secord = 0) 679 | { 680 | long lastTicks = 0; 681 | if (tryGet(key, ref value, ref lastTicks, secord)) { 682 | if (secord == 0) { 683 | _lock.EnterWriteLock(); 684 | } else { 685 | _lock.TryEnterWriteLock(secord * _thousand); 686 | } 687 | try { 688 | _map.Remove(key); 689 | return true; 690 | } finally { _lock.ExitWriteLock(); } 691 | } 692 | return false; 693 | } 694 | private void tryRemove(TKey key, int secord = 0) 695 | { 696 | if (secord == 0) { 697 | _lock.EnterWriteLock(); 698 | } else { 699 | _lock.TryEnterWriteLock(secord * _thousand); 700 | } 701 | try { 702 | _map.Remove(key); 703 | } finally { _lock.ExitWriteLock(); } 704 | } 705 | private bool containsKey(TKey key, int secord = 0) 706 | { 707 | if (secord == 0) { 708 | _lock.EnterReadLock(); 709 | } else { 710 | _lock.TryEnterReadLock(secord * _thousand); 711 | } 712 | try { 713 | Tuple tuple; 714 | if (_map.TryGetValue(key, out tuple)) { 715 | if (_expireTicks == -1) { 716 | return true; 717 | } 718 | if (tuple.Item1 + _expireTicks > DateTime.Now.Ticks) { 719 | return true; 720 | } 721 | } 722 | 723 | } finally { _lock.ExitReadLock(); } 724 | return false; 725 | } 726 | 727 | private AntiDupLockSlim addLock(TKey key) 728 | { 729 | AntiDupLockSlim slim; 730 | _slimLock.EnterWriteLock(); 731 | try { 732 | if (_lockDict.TryGetValue(key, out slim) == false) { 733 | slim = new AntiDupLockSlim(); 734 | _lockDict[key] = slim; 735 | } 736 | slim.UseCount++; 737 | } finally { _slimLock.ExitWriteLock(); } 738 | return slim; 739 | } 740 | 741 | private void removeLock(TKey key, AntiDupLockSlim slim) 742 | { 743 | _slimLock.EnterWriteLock(); 744 | try { 745 | slim.UseCount--; 746 | if (slim.UseCount == 0) { 747 | _lockDict.Remove(key); 748 | slim.Dispose(); 749 | } 750 | } finally { _slimLock.ExitWriteLock(); } 751 | } 752 | 753 | #endregion 754 | 755 | 756 | 757 | 758 | } 759 | 760 | } 761 | -------------------------------------------------------------------------------- /ToolGood.AntiDuplication/AntiDupQueue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace ToolGood.AntiDuplication 7 | { 8 | /// 9 | /// 防重复列队 10 | /// 11 | /// 12 | /// 13 | public class AntiDupQueue : IConcurrentCache 14 | { 15 | private const int _thousand = 1000; 16 | private readonly int _maxCount;//缓存最高数量 17 | private long _lastTicks;//最后Ticks 18 | private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); 19 | private readonly ReaderWriterLockSlim _slimLock = new ReaderWriterLockSlim(); 20 | private readonly Dictionary _map = new Dictionary(); 21 | private readonly Dictionary _lockDict = new Dictionary(); 22 | private readonly Queue _queue = new Queue(); 23 | class AntiDupLockSlim : ReaderWriterLockSlim { public int UseCount; } 24 | 25 | /// 26 | /// 防重复列队 27 | /// 28 | /// 缓存最高数量,0或负数不缓存 29 | public AntiDupQueue(int maxCount = 100) 30 | { 31 | if (maxCount < 0) { 32 | _maxCount = 0; 33 | } else { 34 | _maxCount = maxCount; 35 | } 36 | } 37 | 38 | 39 | 40 | #region 属性 41 | /// 42 | /// 获取缓存个数 43 | /// 44 | public int Count { 45 | get { 46 | _lock.EnterReadLock(); 47 | try { 48 | return _map.Count; 49 | } finally { _lock.ExitReadLock(); } 50 | } 51 | } 52 | 53 | /// 54 | /// 是否为空 55 | /// 56 | public bool IsEmpty { 57 | get { 58 | _lock.EnterReadLock(); 59 | try { 60 | return _map.Count == 0; 61 | } finally { _lock.ExitReadLock(); } 62 | 63 | } 64 | } 65 | #endregion 66 | 67 | #region TValue 68 | 69 | /// 70 | /// 获取或添加缓存 71 | /// 72 | /// 关键字 73 | /// 每次超时秒数 74 | /// 值 75 | /// 76 | public TValue GetOrAdd(TKey key, TValue val, int secord = 0) 77 | { 78 | // 过期时间为0 则不缓存 79 | if (object.Equals(null, key) || _maxCount == 0) { return val; } 80 | 81 | TValue value = default; 82 | long lastTicks = 0; 83 | if (tryGet(key, ref value, ref lastTicks, secord)) { return value; } 84 | 85 | AntiDupLockSlim slim; 86 | _slimLock.EnterUpgradeableReadLock(); 87 | try { 88 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 89 | slim = addLock(key); 90 | } finally { _slimLock.ExitUpgradeableReadLock(); } 91 | 92 | slim.EnterWriteLock(); 93 | try { 94 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 95 | trySet(key, val, secord); 96 | return val; 97 | } finally { 98 | slim.ExitWriteLock(); 99 | removeLock(key, slim); 100 | } 101 | } 102 | 103 | /// 104 | /// 设置缓存 105 | /// 106 | /// 关键字 107 | /// 每次超时秒数 108 | /// 值 109 | public void SetValue(TKey key, TValue val, int secord = 0) 110 | { 111 | if (object.Equals(null, key) || _maxCount == 0) return; 112 | trySet(key, val, secord); 113 | } 114 | 115 | /// 116 | /// 添加或更新缓存 117 | /// 118 | /// 关键字 119 | /// 添加值 120 | /// 更新值 121 | /// 每次超时秒数 122 | /// 123 | public TValue AddOrUpdate(TKey key, TValue addValue, TValue updateValue, int secord = 0) 124 | { 125 | if (object.Equals(null, key) || _maxCount == 0) { return addValue; } 126 | 127 | TValue value = default; 128 | long lastTicks = 0; 129 | if (tryGet(key, ref value, ref lastTicks, secord)) { 130 | trySet(key, updateValue); 131 | return value; 132 | } 133 | 134 | AntiDupLockSlim slim; 135 | _slimLock.EnterUpgradeableReadLock(); 136 | try { 137 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 138 | slim = addLock(key); 139 | } finally { _slimLock.ExitUpgradeableReadLock(); } 140 | 141 | slim.EnterWriteLock(); 142 | try { 143 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 144 | trySet(key, addValue, secord); 145 | return addValue; 146 | } finally { 147 | slim.ExitWriteLock(); 148 | removeLock(key, slim); 149 | } 150 | } 151 | 152 | /// 153 | /// 尝试获取缓存 154 | /// 155 | /// 关键字 156 | /// 每次超时秒数 157 | /// 值 158 | /// 159 | public bool TryGetValue(TKey key, out TValue value, int secord = 0) 160 | { 161 | value = default; 162 | if (object.Equals(null, key) || _maxCount == 0) { return false; } 163 | 164 | long lastTicks = 0; 165 | if (tryGet(key, ref value, ref lastTicks, secord)) { return true; } 166 | return false; 167 | } 168 | 169 | /// 170 | /// 尝试添加缓存 171 | /// 172 | /// 关键字 173 | /// 值 174 | /// 每次超时秒数 175 | /// 176 | public bool TryAdd(TKey key, TValue val, int secord = 0) 177 | { 178 | if (object.Equals(null, key) || _maxCount == 0) { return false; } 179 | 180 | long lastTicks = 0; 181 | TValue value = default; 182 | if (tryGet(key, ref value, ref lastTicks, secord) == false) { 183 | trySet(key, val, secord); 184 | return true; 185 | } 186 | return false; 187 | } 188 | /// 189 | /// 尝试更新缓存 190 | /// 191 | /// 关键字 192 | /// 新值 193 | /// 每次超时秒数 194 | /// 195 | public bool TryUpdate(TKey key, TValue newValue, int secord = 0) 196 | { 197 | if (object.Equals(null, key) || _maxCount == 0) { return false; } 198 | 199 | TValue value = default; 200 | long lastTicks = 0; 201 | if (tryGet(key, ref value, ref lastTicks, secord)) { 202 | trySet(key, newValue); 203 | return true; 204 | } 205 | return false; 206 | } 207 | /// 208 | /// 尝试更新缓存 209 | /// 210 | /// 关键字 211 | /// 新值 212 | /// 原值 213 | /// 每次超时秒数 214 | /// 215 | public bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue, int secord = 0) 216 | { 217 | if (object.Equals(null, key) || _maxCount == 0) { return false; } 218 | 219 | TValue value = default; 220 | long lastTicks = 0; 221 | if (tryGet(key, ref value, ref lastTicks, secord)) { 222 | if (object.Equals(value, comparisonValue)) { 223 | trySet(key, newValue); 224 | } 225 | return true; 226 | } 227 | return false; 228 | } 229 | /// 230 | /// 尝试删除缓存 231 | /// 232 | /// 关键字 233 | /// 值 234 | /// 每次超时秒数 235 | /// 236 | public bool TryRemove(TKey key, out TValue value, int secord = 0) 237 | { 238 | value = default; 239 | if (object.Equals(null, key) || _maxCount == 0) { return false; } 240 | return tryRemove(key, ref value, secord); 241 | } 242 | /// 243 | /// 删除缓存 244 | /// 245 | /// 关键字 246 | /// 每次超时秒数 247 | public void Remove(TKey key, int secord = 0) 248 | { 249 | if (object.Equals(null, key) || _maxCount == 0) { return; } 250 | tryRemove(key, secord); 251 | } 252 | /// 253 | /// 是否包含缓存 254 | /// 255 | /// 256 | /// 257 | /// 258 | public bool ContainsKey(TKey key, int secord = 0) 259 | { 260 | if (object.Equals(null, key) || _maxCount == 0) { return false; } 261 | 262 | return containsKey(key, secord); 263 | } 264 | 265 | #endregion 266 | 267 | #region Func factory 268 | /// 269 | /// 获取或添加缓存 270 | /// 271 | /// 关键字 272 | /// 每次超时秒数 273 | /// 执行方法 274 | /// 275 | public TValue GetOrAdd(TKey key, Func factory, int secord = 0) 276 | { 277 | // 过期时间为0 则不缓存 278 | if (object.Equals(null, key) || _maxCount == 0) { return factory(); } 279 | 280 | TValue value = default; 281 | long lastTicks = 0; 282 | if (tryGet(key, ref value, ref lastTicks, secord)) { return value; } 283 | 284 | AntiDupLockSlim slim; 285 | _slimLock.EnterUpgradeableReadLock(); 286 | try { 287 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 288 | slim = addLock(key); 289 | } finally { _slimLock.ExitUpgradeableReadLock(); } 290 | 291 | slim.EnterWriteLock(); 292 | try { 293 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 294 | var val = factory(); 295 | trySet(key, val, secord); 296 | return val; 297 | } finally { 298 | slim.ExitWriteLock(); 299 | removeLock(key, slim); 300 | } 301 | } 302 | /// 303 | /// 设置缓存 304 | /// 305 | /// 关键字 306 | /// 执行方法 307 | /// 每次超时秒数 308 | /// 309 | public TValue SetValue(TKey key, Func factory, int secord = 0) 310 | { 311 | if (object.Equals(null, key) || _maxCount == 0) return factory(); 312 | var value = factory(); 313 | trySet(key, value, secord); 314 | return value; 315 | } 316 | /// 317 | /// 添加或更新缓存 318 | /// 319 | /// 关键字 320 | /// 添加的值 321 | /// 更新的值 322 | /// 每次超时秒数 323 | /// 324 | public TValue AddOrUpdate(TKey key, Func addValueFactory, Func updateValueFactory, int secord) 325 | { 326 | if (object.Equals(null, key) || _maxCount == 0) { return addValueFactory(); } 327 | 328 | TValue value = default; 329 | long lastTicks = 0; 330 | if (tryGet(key, ref value, ref lastTicks, secord)) { 331 | value = updateValueFactory(); 332 | trySet(key, value); 333 | return value; 334 | } 335 | 336 | AntiDupLockSlim slim; 337 | _slimLock.EnterUpgradeableReadLock(); 338 | try { 339 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 340 | slim = addLock(key); 341 | } finally { _slimLock.ExitUpgradeableReadLock(); } 342 | 343 | slim.EnterWriteLock(); 344 | try { 345 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 346 | var val = addValueFactory(); 347 | trySet(key, val, secord); 348 | return val; 349 | } finally { 350 | slim.ExitWriteLock(); 351 | removeLock(key, slim); 352 | } 353 | } 354 | /// 355 | /// 尝试添加缓存 356 | /// 357 | /// 关键字 358 | /// 359 | /// 每次超时秒数 360 | /// 361 | public bool TryAdd(TKey key, Func factory, int secord = 0) 362 | { 363 | if (object.Equals(null, key) || _maxCount == 0) { return false; } 364 | var value = factory(); 365 | trySet(key, value, secord); 366 | return true; 367 | } 368 | /// 369 | /// 尝试更新缓存 370 | /// 371 | /// 关键字 372 | /// 更新的值 373 | /// 每次超时秒数 374 | /// 375 | public bool TryUpdate(TKey key, Func updateValueFactory, int secord = 0) 376 | { 377 | if (object.Equals(null, key) || _maxCount == 0) { return false; } 378 | 379 | TValue value = default; 380 | long lastTicks = 0; 381 | if (tryGet(key, ref value, ref lastTicks, secord)) { 382 | trySet(key, updateValueFactory(), secord); 383 | return true; 384 | } 385 | return false; 386 | } 387 | /// 388 | /// 尝试更新缓存 389 | /// 390 | /// 关键字 391 | /// 更新的值 392 | /// 原值 393 | /// 每次超时秒数 394 | /// 395 | public bool TryUpdate(TKey key, Func updateValueFactory, TValue comparisonValue, int secord = 0) 396 | { 397 | if (object.Equals(null, key) || _maxCount == 0) { return false; } 398 | 399 | TValue value = default; 400 | long lastTicks = 0; 401 | if (tryGet(key, ref value, ref lastTicks, secord)) { 402 | if (object.Equals(value, comparisonValue)) { 403 | trySet(key, updateValueFactory(), secord); 404 | } 405 | return true; 406 | } 407 | return false; 408 | } 409 | 410 | #endregion 411 | 412 | #region async Func factory 413 | #if !NET40 414 | /// 415 | /// 获取或添加缓存 416 | /// 417 | /// 关键字 418 | /// 每次超时秒数 419 | /// 执行方法 420 | /// 421 | public async Task GetOrAdd(TKey key, Func> factory, int secord = 0) 422 | { 423 | // 过期时间为0 则不缓存 424 | if (object.Equals(null, key) || _maxCount == 0) { return await factory(); } 425 | 426 | TValue value = default; 427 | long lastTicks = 0; 428 | if (tryGet(key, ref value, ref lastTicks, secord)) { return value; } 429 | 430 | AntiDupLockSlim slim; 431 | _slimLock.EnterUpgradeableReadLock(); 432 | try { 433 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 434 | slim = addLock(key); 435 | } finally { _slimLock.ExitUpgradeableReadLock(); } 436 | 437 | slim.EnterWriteLock(); 438 | try { 439 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 440 | var val = await factory(); 441 | trySet(key, val, secord); 442 | return val; 443 | } finally { 444 | slim.ExitWriteLock(); 445 | removeLock(key, slim); 446 | } 447 | } 448 | /// 449 | /// 设置缓存 450 | /// 451 | /// 关键字 452 | /// 执行方法 453 | /// 每次超时秒数 454 | /// 455 | public async Task SetValue(TKey key, Func> factory, int secord = 0) 456 | { 457 | if (object.Equals(null, key) || _maxCount == 0) return await factory(); 458 | var value = await factory(); 459 | trySet(key, value, secord); 460 | return value; 461 | } 462 | /// 463 | /// 添加或更新缓存 464 | /// 465 | /// 关键字 466 | /// 添加的值 467 | /// 更新的值 468 | /// 每次超时秒数 469 | /// 470 | public async Task AddOrUpdate(TKey key, Func> addValueFactory, Func> updateValueFactory, int secord) 471 | { 472 | if (object.Equals(null, key) || _maxCount == 0) { return await addValueFactory(); } 473 | 474 | TValue value = default; 475 | long lastTicks = 0; 476 | if (tryGet(key, ref value, ref lastTicks, secord)) { 477 | value = await updateValueFactory(); 478 | trySet(key, value); 479 | return value; 480 | } 481 | 482 | AntiDupLockSlim slim; 483 | _slimLock.EnterUpgradeableReadLock(); 484 | try { 485 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 486 | slim = addLock(key); 487 | } finally { _slimLock.ExitUpgradeableReadLock(); } 488 | 489 | slim.EnterWriteLock(); 490 | try { 491 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 492 | var val = await addValueFactory(); 493 | trySet(key, val, secord); 494 | return val; 495 | } finally { 496 | slim.ExitWriteLock(); 497 | removeLock(key, slim); 498 | } 499 | } 500 | /// 501 | /// 尝试添加缓存 502 | /// 503 | /// 关键字 504 | /// 505 | /// 每次超时秒数 506 | /// 507 | public async Task TryAdd(TKey key, Func> factory, int secord = 0) 508 | { 509 | if (object.Equals(null, key) || _maxCount == 0) { return false; } 510 | var value = await factory(); 511 | trySet(key, value, secord); 512 | return true; 513 | } 514 | /// 515 | /// 尝试更新缓存 516 | /// 517 | /// 关键字 518 | /// 更新的值 519 | /// 每次超时秒数 520 | /// 521 | public async Task TryUpdate(TKey key, Func> updateValueFactory, int secord = 0) 522 | { 523 | if (object.Equals(null, key) || _maxCount == 0) { return false; } 524 | 525 | TValue value = default; 526 | long lastTicks = 0; 527 | if (tryGet(key, ref value, ref lastTicks, secord)) { 528 | trySet(key, await updateValueFactory(), secord); 529 | return true; 530 | } 531 | return false; 532 | } 533 | /// 534 | /// 尝试更新缓存 535 | /// 536 | /// 关键字 537 | /// 更新的值 538 | /// 原值 539 | /// 每次超时秒数 540 | /// 541 | public async Task TryUpdate(TKey key, Func> updateValueFactory, TValue comparisonValue, int secord = 0) 542 | { 543 | if (object.Equals(null, key) || _maxCount == 0) { return false; } 544 | 545 | TValue value = default; 546 | long lastTicks = 0; 547 | if (tryGet(key, ref value, ref lastTicks, secord)) { 548 | if (object.Equals(value, comparisonValue)) { 549 | trySet(key, await updateValueFactory(), secord); 550 | } 551 | return true; 552 | } 553 | return false; 554 | } 555 | /// 556 | /// 尝试更新缓存 557 | /// 558 | /// 关键字 559 | /// 更新的值 560 | /// 原值 561 | /// 每次超时秒数 562 | /// 563 | public async Task TryUpdate(TKey key, Func> updateValueFactory, Func> comparisonValueFactory, int secord = 0) 564 | { 565 | if (object.Equals(null, key) || _maxCount == 0) { return false; } 566 | 567 | TValue value = default; 568 | long lastTicks = 0; 569 | if (tryGet(key, ref value, ref lastTicks, secord)) { 570 | if (object.Equals(value, await comparisonValueFactory())) { 571 | trySet(key, await updateValueFactory(), secord); 572 | } 573 | return true; 574 | } 575 | return false; 576 | } 577 | 578 | #endif 579 | #endregion 580 | 581 | #region 清空 582 | /// 583 | /// 清空 584 | /// 585 | public void Clear() 586 | { 587 | _lock.EnterWriteLock(); 588 | try { 589 | _map.Clear(); 590 | _queue.Clear(); 591 | _slimLock.EnterWriteLock(); 592 | try { 593 | _lockDict.Clear(); 594 | } finally { 595 | _slimLock.ExitWriteLock(); 596 | } 597 | } finally { 598 | _lock.ExitWriteLock(); 599 | } 600 | } 601 | #endregion 602 | 603 | #region private 方法 604 | private bool tryGet(TKey key, ref TValue value, ref long lastTicks, int secord = 0) 605 | { 606 | if (secord == 0) { 607 | _lock.EnterReadLock(); 608 | } else { 609 | _lock.TryEnterReadLock(secord * _thousand); 610 | } 611 | try { 612 | if (_map.TryGetValue(key, out value)) { 613 | return true; 614 | } 615 | lastTicks = _lastTicks; 616 | } finally { _lock.ExitReadLock(); } 617 | return false; 618 | } 619 | 620 | private bool checkGet(TKey key, long lastTicks, ref TValue value, int secord = 0) 621 | { 622 | if (secord == 0) { 623 | _lock.EnterReadLock(); 624 | } else { 625 | _lock.TryEnterReadLock(secord * _thousand); 626 | } 627 | try { 628 | if (_lastTicks != lastTicks && _map.TryGetValue(key, out value)) { 629 | return true; 630 | } 631 | } finally { _lock.ExitReadLock(); } 632 | return false; 633 | } 634 | 635 | private void trySet(TKey key, TValue value, int secord = 0) 636 | { 637 | if (secord == 0) { 638 | _lock.EnterWriteLock(); 639 | } else { 640 | _lock.TryEnterWriteLock(secord * _thousand); 641 | } 642 | try { 643 | _map[key] = value; 644 | if (_maxCount > 0) { 645 | if (_queue.Contains(key) == false) { 646 | _queue.Enqueue(key); 647 | if (_queue.Count > _maxCount) _map.Remove(_queue.Dequeue()); 648 | } 649 | } 650 | } finally { _lock.ExitWriteLock(); } 651 | } 652 | 653 | private bool tryRemove(TKey key, ref TValue value, int secord = 0) 654 | { 655 | long lastTicks = 0; 656 | if (tryGet(key, ref value, ref lastTicks, secord)) { 657 | if (secord == 0) { 658 | _lock.EnterWriteLock(); 659 | } else { 660 | _lock.TryEnterWriteLock(secord * _thousand); 661 | } 662 | try { 663 | _map.Remove(key); 664 | return true; 665 | } finally { _lock.ExitWriteLock(); } 666 | } 667 | return false; 668 | } 669 | private void tryRemove(TKey key, int secord = 0) 670 | { 671 | if (secord == 0) { 672 | _lock.EnterWriteLock(); 673 | } else { 674 | _lock.TryEnterWriteLock(secord * _thousand); 675 | } 676 | try { 677 | _map.Remove(key); 678 | } finally { _lock.ExitWriteLock(); } 679 | } 680 | 681 | private bool containsKey(TKey key, int secord = 0) 682 | { 683 | if (secord == 0) { 684 | _lock.EnterReadLock(); 685 | } else { 686 | _lock.TryEnterReadLock(secord * _thousand); 687 | } 688 | try { 689 | return _map.ContainsKey(key); 690 | } finally { _lock.ExitReadLock(); } 691 | } 692 | 693 | 694 | 695 | private AntiDupLockSlim addLock(TKey key) 696 | { 697 | AntiDupLockSlim slim; 698 | _slimLock.EnterWriteLock(); 699 | try { 700 | if (_lockDict.TryGetValue(key, out slim) == false) { 701 | slim = new AntiDupLockSlim(); 702 | _lockDict[key] = slim; 703 | } 704 | slim.UseCount++; 705 | } finally { _slimLock.ExitWriteLock(); } 706 | return slim; 707 | } 708 | 709 | private void removeLock(TKey key, AntiDupLockSlim slim) 710 | { 711 | _slimLock.EnterWriteLock(); 712 | try { 713 | slim.UseCount--; 714 | if (slim.UseCount == 0) { 715 | _lockDict.Remove(key); 716 | slim.Dispose(); 717 | } 718 | } finally { _slimLock.ExitWriteLock(); } 719 | } 720 | 721 | 722 | #endregion 723 | 724 | } 725 | } 726 | -------------------------------------------------------------------------------- /ToolGood.AntiDuplication/DictCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace ToolGood.AntiDuplication 9 | { 10 | /// 11 | /// 字典缓存 12 | /// 13 | /// 14 | /// 15 | public class DictCache : IConcurrentCache 16 | { 17 | private const int _thousand = 1000; 18 | private long _lastTicks;//最后Ticks 19 | private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); 20 | private readonly ReaderWriterLockSlim _slimLock = new ReaderWriterLockSlim(); 21 | private readonly Dictionary _map = new Dictionary(); 22 | private readonly Dictionary _lockDict = new Dictionary(); 23 | class AntiDupLockSlim : ReaderWriterLockSlim { public int UseCount; } 24 | 25 | 26 | #region 属性 27 | /// 28 | /// 获取缓存个数 29 | /// 30 | public int Count { 31 | get { 32 | _lock.EnterReadLock(); 33 | try { 34 | return _map.Count; 35 | } finally { _lock.ExitReadLock(); } 36 | } 37 | } 38 | 39 | /// 40 | /// 是否为空 41 | /// 42 | public bool IsEmpty { 43 | get { 44 | _lock.EnterReadLock(); 45 | try { 46 | return _map.Count == 0; 47 | } finally { _lock.ExitReadLock(); } 48 | 49 | } 50 | } 51 | #endregion 52 | 53 | #region TValue 54 | 55 | /// 56 | /// 获取或添加缓存 57 | /// 58 | /// 关键字 59 | /// 每次超时秒数 60 | /// 值 61 | /// 62 | public TValue GetOrAdd(TKey key, TValue val, int secord = 0) 63 | { 64 | // 过期时间为0 则不缓存 65 | if (object.Equals(null, key)) { return val; } 66 | 67 | TValue value = default; 68 | long lastTicks = 0; 69 | if (tryGet(key, ref value, ref lastTicks, secord)) { return value; } 70 | 71 | AntiDupLockSlim slim; 72 | _slimLock.EnterUpgradeableReadLock(); 73 | try { 74 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 75 | slim = addLock(key); 76 | } finally { _slimLock.ExitUpgradeableReadLock(); } 77 | 78 | slim.EnterWriteLock(); 79 | try { 80 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 81 | trySet(key, val, secord); 82 | return val; 83 | } finally { 84 | slim.ExitWriteLock(); 85 | removeLock(key, slim); 86 | } 87 | } 88 | 89 | /// 90 | /// 设置缓存 91 | /// 92 | /// 关键字 93 | /// 每次超时秒数 94 | /// 值 95 | public void SetValue(TKey key, TValue value, int secord = 0) 96 | { 97 | if (object.Equals(null, key)) return; 98 | trySet(key, value, secord); 99 | } 100 | 101 | /// 102 | /// 添加或更新缓存 103 | /// 104 | /// 关键字 105 | /// 添加值 106 | /// 更新值 107 | /// 每次超时秒数 108 | /// 109 | public TValue AddOrUpdate(TKey key, TValue addValue, TValue updateValue, int secord = 0) 110 | { 111 | if (object.Equals(null, key)) { return addValue; } 112 | 113 | TValue value = default; 114 | long lastTicks = 0; 115 | if (tryGet(key, ref value, ref lastTicks, secord)) { 116 | trySet(key, updateValue); 117 | return value; 118 | } 119 | 120 | AntiDupLockSlim slim; 121 | _slimLock.EnterUpgradeableReadLock(); 122 | try { 123 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 124 | slim = addLock(key); 125 | } finally { _slimLock.ExitUpgradeableReadLock(); } 126 | 127 | slim.EnterWriteLock(); 128 | try { 129 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 130 | trySet(key, addValue, secord); 131 | return addValue; 132 | } finally { 133 | slim.ExitWriteLock(); 134 | removeLock(key, slim); 135 | } 136 | } 137 | 138 | /// 139 | /// 尝试获取缓存 140 | /// 141 | /// 关键字 142 | /// 每次超时秒数 143 | /// 值 144 | /// 145 | public bool TryGetValue(TKey key, out TValue value, int secord = 0) 146 | { 147 | value = default; 148 | if (object.Equals(null, key)) { return false; } 149 | 150 | long lastTicks = 0; 151 | if (tryGet(key, ref value, ref lastTicks, secord)) { return true; } 152 | return false; 153 | } 154 | 155 | /// 156 | /// 尝试添加缓存 157 | /// 158 | /// 关键字 159 | /// 值 160 | /// 每次超时秒数 161 | /// 162 | public bool TryAdd(TKey key, TValue val, int secord = 0) 163 | { 164 | if (object.Equals(null, key)) { return false; } 165 | 166 | long lastTicks = 0; 167 | TValue value = default; 168 | if (tryGet(key, ref value, ref lastTicks, secord) == false) { 169 | trySet(key, val, secord); 170 | return true; 171 | } 172 | return false; 173 | } 174 | /// 175 | /// 尝试更新缓存 176 | /// 177 | /// 关键字 178 | /// 新值 179 | /// 每次超时秒数 180 | /// 181 | public bool TryUpdate(TKey key, TValue newValue, int secord = 0) 182 | { 183 | if (object.Equals(null, key)) { return false; } 184 | 185 | TValue value = default; 186 | long lastTicks = 0; 187 | if (tryGet(key, ref value, ref lastTicks, secord)) { 188 | trySet(key, newValue); 189 | return true; 190 | } 191 | return false; 192 | } 193 | /// 194 | /// 尝试更新缓存 195 | /// 196 | /// 关键字 197 | /// 新值 198 | /// 原值 199 | /// 每次超时秒数 200 | /// 201 | public bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue, int secord = 0) 202 | { 203 | if (object.Equals(null, key)) { return false; } 204 | 205 | TValue value = default; 206 | long lastTicks = 0; 207 | if (tryGet(key, ref value, ref lastTicks, secord)) { 208 | if (object.Equals(value, comparisonValue)) { 209 | trySet(key, newValue); 210 | } 211 | return true; 212 | } 213 | return false; 214 | } 215 | /// 216 | /// 尝试删除缓存 217 | /// 218 | /// 关键字 219 | /// 值 220 | /// 每次超时秒数 221 | /// 222 | public bool TryRemove(TKey key, out TValue value, int secord = 0) 223 | { 224 | value = default; 225 | if (object.Equals(null, key)) { return false; } 226 | return tryRemove(key, ref value, secord); 227 | } 228 | /// 229 | /// 删除缓存 230 | /// 231 | /// 关键字 232 | /// 每次超时秒数 233 | public void Remove(TKey key, int secord = 0) 234 | { 235 | if (object.Equals(null, key)) { return; } 236 | tryRemove(key, secord); 237 | } 238 | /// 239 | /// 是否包含缓存 240 | /// 241 | /// 242 | /// 243 | /// 244 | public bool ContainsKey(TKey key, int secord = 0) 245 | { 246 | if (object.Equals(null, key)) { return false; } 247 | 248 | return containsKey(key, secord); 249 | } 250 | 251 | #endregion 252 | 253 | #region Func factory 254 | /// 255 | /// 获取或添加缓存 256 | /// 257 | /// 关键字 258 | /// 每次超时秒数 259 | /// 执行方法 260 | /// 261 | public TValue GetOrAdd(TKey key, Func factory, int secord = 0) 262 | { 263 | // 过期时间为0 则不缓存 264 | if (object.Equals(null, key)) { return factory(); } 265 | 266 | TValue value = default; 267 | long lastTicks = 0; 268 | if (tryGet(key, ref value, ref lastTicks, secord)) { return value; } 269 | 270 | AntiDupLockSlim slim; 271 | _slimLock.EnterUpgradeableReadLock(); 272 | try { 273 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 274 | slim = addLock(key); 275 | } finally { _slimLock.ExitUpgradeableReadLock(); } 276 | 277 | slim.EnterWriteLock(); 278 | try { 279 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 280 | var val = factory(); 281 | trySet(key, val, secord); 282 | return val; 283 | } finally { 284 | slim.ExitWriteLock(); 285 | removeLock(key, slim); 286 | } 287 | } 288 | /// 289 | /// 设置缓存 290 | /// 291 | /// 关键字 292 | /// 执行方法 293 | /// 每次超时秒数 294 | /// 295 | public TValue SetValue(TKey key, Func factory, int secord = 0) 296 | { 297 | if (object.Equals(null, key)) return factory(); 298 | var value = factory(); 299 | trySet(key, value, secord); 300 | return value; 301 | } 302 | /// 303 | /// 添加或更新缓存 304 | /// 305 | /// 关键字 306 | /// 添加的值 307 | /// 更新的值 308 | /// 每次超时秒数 309 | /// 310 | public TValue AddOrUpdate(TKey key, Func addValueFactory, Func updateValueFactory, int secord) 311 | { 312 | if (object.Equals(null, key)) { return addValueFactory(); } 313 | 314 | TValue value = default; 315 | long lastTicks = 0; 316 | if (tryGet(key, ref value, ref lastTicks, secord)) { 317 | value = updateValueFactory(); 318 | trySet(key, value); 319 | return value; 320 | } 321 | 322 | AntiDupLockSlim slim; 323 | _slimLock.EnterUpgradeableReadLock(); 324 | try { 325 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 326 | slim = addLock(key); 327 | } finally { _slimLock.ExitUpgradeableReadLock(); } 328 | 329 | slim.EnterWriteLock(); 330 | try { 331 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 332 | var val = addValueFactory(); 333 | trySet(key, val, secord); 334 | return val; 335 | } finally { 336 | slim.ExitWriteLock(); 337 | removeLock(key, slim); 338 | } 339 | } 340 | /// 341 | /// 尝试添加缓存 342 | /// 343 | /// 关键字 344 | /// 345 | /// 每次超时秒数 346 | /// 347 | public bool TryAdd(TKey key, Func factory, int secord = 0) 348 | { 349 | if (object.Equals(null, key)) { return false; } 350 | var value = factory(); 351 | trySet(key, value, secord); 352 | return true; 353 | } 354 | /// 355 | /// 尝试更新缓存 356 | /// 357 | /// 关键字 358 | /// 更新的值 359 | /// 每次超时秒数 360 | /// 361 | public bool TryUpdate(TKey key, Func updateValueFactory, int secord = 0) 362 | { 363 | if (object.Equals(null, key)) { return false; } 364 | 365 | TValue value = default; 366 | long lastTicks = 0; 367 | if (tryGet(key, ref value, ref lastTicks, secord)) { 368 | trySet(key, updateValueFactory(), secord); 369 | return true; 370 | } 371 | return false; 372 | } 373 | /// 374 | /// 尝试更新缓存 375 | /// 376 | /// 关键字 377 | /// 更新的值 378 | /// 原值 379 | /// 每次超时秒数 380 | /// 381 | public bool TryUpdate(TKey key, Func updateValueFactory, TValue comparisonValue, int secord = 0) 382 | { 383 | if (object.Equals(null, key)) { return false; } 384 | 385 | TValue value = default; 386 | long lastTicks = 0; 387 | if (tryGet(key, ref value, ref lastTicks, secord)) { 388 | if (object.Equals(value, comparisonValue)) { 389 | trySet(key, updateValueFactory(), secord); 390 | } 391 | return true; 392 | } 393 | return false; 394 | } 395 | 396 | #endregion 397 | 398 | #region async Func factory 399 | #if !NET40 400 | /// 401 | /// 获取或添加缓存 402 | /// 403 | /// 关键字 404 | /// 每次超时秒数 405 | /// 执行方法 406 | /// 407 | public async Task GetOrAdd(TKey key, Func> factory, int secord = 0) 408 | { 409 | // 过期时间为0 则不缓存 410 | if (object.Equals(null, key)) { return await factory(); } 411 | 412 | TValue value = default; 413 | long lastTicks = 0; 414 | if (tryGet(key, ref value, ref lastTicks, secord)) { return value; } 415 | 416 | AntiDupLockSlim slim; 417 | _slimLock.EnterUpgradeableReadLock(); 418 | try { 419 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 420 | slim = addLock(key); 421 | } finally { _slimLock.ExitUpgradeableReadLock(); } 422 | 423 | slim.EnterWriteLock(); 424 | try { 425 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 426 | var val = await factory(); 427 | trySet(key, val, secord); 428 | return val; 429 | } finally { 430 | slim.ExitWriteLock(); 431 | removeLock(key, slim); 432 | } 433 | } 434 | /// 435 | /// 设置缓存 436 | /// 437 | /// 关键字 438 | /// 执行方法 439 | /// 每次超时秒数 440 | /// 441 | public async Task SetValue(TKey key, Func> factory, int secord = 0) 442 | { 443 | if (object.Equals(null, key)) return await factory(); 444 | var value = await factory(); 445 | trySet(key, value, secord); 446 | return value; 447 | } 448 | /// 449 | /// 添加或更新缓存 450 | /// 451 | /// 关键字 452 | /// 添加的值 453 | /// 更新的值 454 | /// 每次超时秒数 455 | /// 456 | public async Task AddOrUpdate(TKey key, Func> addValueFactory, Func> updateValueFactory, int secord) 457 | { 458 | if (object.Equals(null, key)) { return await addValueFactory(); } 459 | 460 | TValue value = default; 461 | long lastTicks = 0; 462 | if (tryGet(key, ref value, ref lastTicks, secord)) { 463 | value = await updateValueFactory(); 464 | trySet(key, value); 465 | return value; 466 | } 467 | 468 | AntiDupLockSlim slim; 469 | _slimLock.EnterUpgradeableReadLock(); 470 | try { 471 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 472 | slim = addLock(key); 473 | } finally { _slimLock.ExitUpgradeableReadLock(); } 474 | 475 | slim.EnterWriteLock(); 476 | try { 477 | if (checkGet(key, lastTicks, ref value, secord)) { return value; } 478 | var val = await addValueFactory(); 479 | trySet(key, val, secord); 480 | return val; 481 | } finally { 482 | slim.ExitWriteLock(); 483 | removeLock(key, slim); 484 | } 485 | } 486 | /// 487 | /// 尝试添加缓存 488 | /// 489 | /// 关键字 490 | /// 491 | /// 每次超时秒数 492 | /// 493 | public async Task TryAdd(TKey key, Func> factory, int secord = 0) 494 | { 495 | if (object.Equals(null, key)) { return false; } 496 | var value = await factory(); 497 | trySet(key, value, secord); 498 | return true; 499 | } 500 | /// 501 | /// 尝试更新缓存 502 | /// 503 | /// 关键字 504 | /// 更新的值 505 | /// 每次超时秒数 506 | /// 507 | public async Task TryUpdate(TKey key, Func> updateValueFactory, int secord = 0) 508 | { 509 | if (object.Equals(null, key)) { return false; } 510 | 511 | TValue value = default; 512 | long lastTicks = 0; 513 | if (tryGet(key, ref value, ref lastTicks, secord)) { 514 | trySet(key, await updateValueFactory(), secord); 515 | return true; 516 | } 517 | return false; 518 | } 519 | /// 520 | /// 尝试更新缓存 521 | /// 522 | /// 关键字 523 | /// 更新的值 524 | /// 原值 525 | /// 每次超时秒数 526 | /// 527 | public async Task TryUpdate(TKey key, Func> updateValueFactory, TValue comparisonValue, int secord = 0) 528 | { 529 | if (object.Equals(null, key)) { return false; } 530 | 531 | TValue value = default; 532 | long lastTicks = 0; 533 | if (tryGet(key, ref value, ref lastTicks, secord)) { 534 | if (object.Equals(value, comparisonValue)) { 535 | trySet(key, await updateValueFactory(), secord); 536 | } 537 | return true; 538 | } 539 | return false; 540 | } 541 | /// 542 | /// 尝试更新缓存 543 | /// 544 | /// 关键字 545 | /// 更新的值 546 | /// 原值 547 | /// 每次超时秒数 548 | /// 549 | public async Task TryUpdate(TKey key, Func> updateValueFactory, Func> comparisonValueFactory, int secord = 0) 550 | { 551 | if (object.Equals(null, key)) { return false; } 552 | 553 | TValue value = default; 554 | long lastTicks = 0; 555 | if (tryGet(key, ref value, ref lastTicks, secord)) { 556 | if (object.Equals(value, await comparisonValueFactory())) { 557 | trySet(key, await updateValueFactory(), secord); 558 | } 559 | return true; 560 | } 561 | return false; 562 | } 563 | 564 | #endif 565 | #endregion 566 | 567 | #region 清空 568 | /// 569 | /// 清空 570 | /// 571 | public void Clear() 572 | { 573 | _lock.EnterWriteLock(); 574 | try { 575 | _map.Clear(); 576 | _slimLock.EnterWriteLock(); 577 | try { 578 | _lockDict.Clear(); 579 | } finally { 580 | _slimLock.ExitWriteLock(); 581 | } 582 | } finally { 583 | _lock.ExitWriteLock(); 584 | } 585 | } 586 | #endregion 587 | 588 | 589 | #region private 方法 590 | private bool tryGet(TKey key, ref TValue value, ref long lastTicks, int secord = 0) 591 | { 592 | if (secord == 0) { 593 | _lock.EnterReadLock(); 594 | } else { 595 | _lock.TryEnterReadLock(secord * _thousand); 596 | } 597 | try { 598 | if (_map.TryGetValue(key, out value)) { 599 | return true; 600 | } 601 | lastTicks = _lastTicks; 602 | } finally { _lock.ExitReadLock(); } 603 | return false; 604 | } 605 | 606 | private bool checkGet(TKey key, long lastTicks, ref TValue value, int secord = 0) 607 | { 608 | if (secord == 0) { 609 | _lock.EnterReadLock(); 610 | } else { 611 | _lock.TryEnterReadLock(secord * _thousand); 612 | } 613 | try { 614 | if (_lastTicks != lastTicks && _map.TryGetValue(key, out value)) { 615 | return true; 616 | } 617 | } finally { _lock.ExitReadLock(); } 618 | return false; 619 | } 620 | 621 | private void trySet(TKey key, TValue value, int secord = 0) 622 | { 623 | if (secord == 0) { 624 | _lock.EnterWriteLock(); 625 | } else { 626 | _lock.TryEnterWriteLock(secord * _thousand); 627 | } 628 | try { 629 | _map[key] = value; 630 | } finally { _lock.ExitWriteLock(); } 631 | } 632 | 633 | private bool tryRemove(TKey key, ref TValue value, int secord = 0) 634 | { 635 | long lastTicks = 0; 636 | if (tryGet(key, ref value, ref lastTicks, secord)) { 637 | if (secord == 0) { 638 | _lock.EnterWriteLock(); 639 | } else { 640 | _lock.TryEnterWriteLock(secord * _thousand); 641 | } 642 | try { 643 | _map.Remove(key); 644 | return true; 645 | } finally { _lock.ExitWriteLock(); } 646 | } 647 | return false; 648 | } 649 | private void tryRemove(TKey key, int secord = 0) 650 | { 651 | if (secord == 0) { 652 | _lock.EnterWriteLock(); 653 | } else { 654 | _lock.TryEnterWriteLock(secord * _thousand); 655 | } 656 | try { 657 | _map.Remove(key); 658 | } finally { _lock.ExitWriteLock(); } 659 | } 660 | 661 | private bool containsKey(TKey key, int secord = 0) 662 | { 663 | if (secord == 0) { 664 | _lock.EnterReadLock(); 665 | } else { 666 | _lock.TryEnterReadLock(secord * _thousand); 667 | } 668 | try { 669 | return _map.ContainsKey(key); 670 | } finally { _lock.ExitReadLock(); } 671 | } 672 | 673 | private AntiDupLockSlim addLock(TKey key) 674 | { 675 | AntiDupLockSlim slim; 676 | _slimLock.EnterWriteLock(); 677 | try { 678 | if (_lockDict.TryGetValue(key, out slim) == false) { 679 | slim = new AntiDupLockSlim(); 680 | _lockDict[key] = slim; 681 | } 682 | slim.UseCount++; 683 | } finally { _slimLock.ExitWriteLock(); } 684 | return slim; 685 | } 686 | 687 | private void removeLock(TKey key, AntiDupLockSlim slim) 688 | { 689 | _slimLock.EnterWriteLock(); 690 | try { 691 | slim.UseCount--; 692 | if (slim.UseCount == 0) { 693 | _lockDict.Remove(key); 694 | slim.Dispose(); 695 | } 696 | } finally { _slimLock.ExitWriteLock(); } 697 | } 698 | #endregion 699 | } 700 | } 701 | -------------------------------------------------------------------------------- /ToolGood.AntiDuplication/IConcurrentCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ToolGood.AntiDuplication 8 | { 9 | /// 10 | /// 执行缓存接口 11 | /// 12 | /// 13 | /// 14 | public interface IConcurrentCache 15 | { 16 | 17 | /// 18 | /// 获取缓存个数 19 | /// 20 | int Count { get; } 21 | 22 | /// 23 | /// 是否为空 24 | /// 25 | bool IsEmpty { get; } 26 | 27 | #region TValue 28 | 29 | /// 30 | /// 获取或添加缓存 31 | /// 32 | /// 关键字 33 | /// 每次超时秒数 34 | /// 值 35 | /// 36 | TValue GetOrAdd(TKey key, TValue val, int secord = 0); 37 | 38 | /// 39 | /// 设置缓存 40 | /// 41 | /// 关键字 42 | /// 每次超时秒数 43 | /// 值 44 | void SetValue(TKey key, TValue value, int secord = 0); 45 | 46 | /// 47 | /// 添加或更新缓存 48 | /// 49 | /// 关键字 50 | /// 添加值 51 | /// 更新值 52 | /// 每次超时秒数 53 | /// 54 | TValue AddOrUpdate(TKey key, TValue addValue, TValue updateValue, int secord = 0); 55 | 56 | /// 57 | /// 尝试获取缓存 58 | /// 59 | /// 关键字 60 | /// 每次超时秒数 61 | /// 值 62 | /// 63 | bool TryGetValue(TKey key, out TValue value, int secord = 0); 64 | 65 | /// 66 | /// 尝试添加缓存 67 | /// 68 | /// 关键字 69 | /// 值 70 | /// 每次超时秒数 71 | /// 72 | bool TryAdd(TKey key, TValue value, int secord = 0); 73 | /// 74 | /// 尝试更新缓存 75 | /// 76 | /// 关键字 77 | /// 新值 78 | /// 每次超时秒数 79 | /// 80 | bool TryUpdate(TKey key, TValue newValue, int secord = 0); 81 | 82 | /// 83 | /// 尝试更新缓存 84 | /// 85 | /// 关键字 86 | /// 新值 87 | /// 原值 88 | /// 每次超时秒数 89 | /// 90 | bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue, int secord = 0); 91 | 92 | /// 93 | /// 尝试删除缓存 94 | /// 95 | /// 关键字 96 | /// 值 97 | /// 每次超时秒数 98 | /// 99 | bool TryRemove(TKey key, out TValue value, int secord = 0); 100 | 101 | /// 102 | /// 删除缓存 103 | /// 104 | /// 关键字 105 | /// 每次超时秒数 106 | void Remove(TKey key, int secord = 0); 107 | 108 | /// 109 | /// 是否包含缓存 110 | /// 111 | /// 112 | /// 113 | /// 114 | bool ContainsKey(TKey key, int secord = 0); 115 | 116 | #endregion 117 | 118 | #region Func factory 119 | /// 120 | /// 获取或添加缓存 121 | /// 122 | /// 关键字 123 | /// 每次超时秒数 124 | /// 执行方法 125 | /// 126 | TValue GetOrAdd(TKey key, Func factory, int secord = 0); 127 | 128 | /// 129 | /// 设置缓存 130 | /// 131 | /// 关键字 132 | /// 执行方法 133 | /// 每次超时秒数 134 | /// 135 | TValue SetValue(TKey key, Func factory, int secord = 0); 136 | 137 | /// 138 | /// 添加或更新缓存 139 | /// 140 | /// 关键字 141 | /// 添加的值 142 | /// 更新的值 143 | /// 每次超时秒数 144 | /// 145 | TValue AddOrUpdate(TKey key, Func addValueFactory, Func updateValueFactory, int secord); 146 | 147 | /// 148 | /// 尝试添加缓存 149 | /// 150 | /// 关键字 151 | /// 152 | /// 每次超时秒数 153 | /// 154 | bool TryAdd(TKey key, Func factory, int secord = 0); 155 | /// 156 | /// 尝试更新缓存 157 | /// 158 | /// 关键字 159 | /// 更新的值 160 | /// 每次超时秒数 161 | /// 162 | bool TryUpdate(TKey key, Func updateValueFactory, int secord = 0); 163 | 164 | /// 165 | /// 尝试更新缓存 166 | /// 167 | /// 关键字 168 | /// 更新的值 169 | /// 原值 170 | /// 每次超时秒数 171 | /// 172 | bool TryUpdate(TKey key, Func updateValueFactory, TValue comparisonValue, int secord = 0); 173 | 174 | #endregion 175 | 176 | #region Func factory 177 | #if !NET40 178 | /// 179 | /// 获取或添加缓存 180 | /// 181 | /// 关键字 182 | /// 每次超时秒数 183 | /// 执行方法 184 | /// 185 | Task GetOrAdd(TKey key, Func> factory, int secord = 0); 186 | 187 | /// 188 | /// 设置缓存 189 | /// 190 | /// 关键字 191 | /// 执行方法 192 | /// 每次超时秒数 193 | /// 194 | Task SetValue(TKey key, Func> factory, int secord = 0); 195 | 196 | /// 197 | /// 添加或更新缓存 198 | /// 199 | /// 关键字 200 | /// 添加的值 201 | /// 更新的值 202 | /// 每次超时秒数 203 | /// 204 | Task AddOrUpdate(TKey key, Func> addValueFactory, Func> updateValueFactory, int secord); 205 | 206 | /// 207 | /// 尝试添加缓存 208 | /// 209 | /// 关键字 210 | /// 211 | /// 每次超时秒数 212 | /// 213 | Task TryAdd(TKey key, Func> factory, int secord = 0); 214 | /// 215 | /// 尝试更新缓存 216 | /// 217 | /// 关键字 218 | /// 更新的值 219 | /// 每次超时秒数 220 | /// 221 | Task TryUpdate(TKey key, Func> updateValueFactory, int secord = 0); 222 | /// 223 | /// 尝试更新缓存 224 | /// 225 | /// 关键字 226 | /// 更新的值 227 | /// 原值 228 | /// 每次超时秒数 229 | /// 230 | Task TryUpdate(TKey key, Func> updateValueFactory, TValue comparisonValue, int secord = 0); 231 | /// 232 | /// 尝试更新缓存 233 | /// 234 | /// 关键字 235 | /// 更新的值 236 | /// 原值 237 | /// 每次超时秒数 238 | /// 239 | Task TryUpdate(TKey key, Func> updateValueFactory, Func> comparisonValueFactory, int secord = 0); 240 | 241 | #endif 242 | #endregion 243 | 244 | /// 245 | /// 清空 246 | /// 247 | void Clear(); 248 | 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /ToolGood.AntiDuplication/ToolGood.AntiDuplication.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net40;net45;netstandard2.0; 5 | true 6 | key.snk 7 | true 8 | false 9 | ToolGood 10 | ToolGood.com 11 | Anti duplication submit 。防重复提交。 12 | Copyright 2016-2019 ToolGood 13 | 14 | https://github.com/toolgood/ToolGood.AntiDuplication 15 | https://github.com/toolgood/ToolGood.AntiDuplication.git 16 | git 17 | mvc;Anti duplication submit 18 | 2.0.0.1 19 | 2.0.0.1 20 | 2.0.0.1 21 | LICENSE 22 | 23 | 24 | 25 | 26 | 27 | bin\Release\net40\ToolGood.AntiDuplication.xml 28 | 1701;1702;1705:1591 29 | 30 | 31 | bin\Release\net45\ToolGood.AntiDuplication.xml 32 | 33 | 34 | bin\Release\netstandard2.0\ToolGood.AntiDuplication.xml 35 | 36 | 37 | 38 | bin\Debug\net40\ToolGood.AntiDuplication.xml 39 | 40 | 41 | bin\Debug\net45\ToolGood.AntiDuplication.xml 42 | 43 | 44 | bin\Debug\netstandard2.0\ToolGood.AntiDuplication.xml 45 | 46 | 47 | 48 | True 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /ToolGood.AntiDuplication/key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toolgood/ToolGood.AntiDuplication/19874b5708180c1948a5ce9d583751e14dc3ca3f/ToolGood.AntiDuplication/key.snk --------------------------------------------------------------------------------